Vuejs 2 using filterBy and orderBy in the same computed property - sorting

I am having a hard time trying to joing a filterBy with orderBy, on vuejs 2.0, with all research I have found about this subject, as of link on the bottom of my question.
This is my filter, which is working:
// computed() {...
filteredResults() {
var self = this
return self.results
.filter(result => result.name.indexOf(self.filterName) !== -1)
}
A method called in the component:
// methods() {...
customFilter(ev, property, value) {
ev.preventDefault()
this.filterBook = value
}
In the component:
// Inside my component
Name..
And another filter, which works as well:
// computed() {...
orderByResults: function() {
return _.orderBy(this.results, this.sortProperty, this.sortDirection)
}
To comply with my orderBy I have this method:
// methods() {...
sort(ev, property) {
ev.preventDefault()
if (this.sortDirection == 'asc' && this.sortProperty == property ) {
this.sortDirection = 'desc'
} else {
this.sortDirection = 'asc'
}
this.sortProperty = property
}
And to call it I have the following:
// Inside my component
Name..
I have found in the docs how we use this OrderBy, and in this very long conversation how to use filter joint with sort, but I could really not implement it...
Which should be some like this:
filteredThings () {
return this.things
.filter(item => item.title.indexOf('foo') > -1)
.sort((a, b) => a.bar > b.bar ? 1 : -1)
.slice(0, 5)
}
I could not make this work...
I tried in many forms as of:
.sort((self.sortProperty, self.sortDirection) => this.sortDirection == 'asc' && this.sortProperty == property ? this.sortDirection = 'desc' : this.sortDirection = 'asc' )
But still, or it does not compile or it comes with errors, such as:
property not defined (which is defines such as I am using it in the other method)
method of funcion not found (is happens when comment my method sort.. maybe here is what I am missing something)
Thanks for any help!

The ideas of your approach seem valid, but without a full example it's hard to tell what might actually be wrong.
Here's a simple example of sorting and filtering combined. The code can easily be extended e.g. to work with arbitrary fields in the test data. The filtering and sorting is done in the same computed property, based on the parameters set from the outside. Here's a working JSFiddle.
<div id="app">
<div>{{filteredAndSortedData}}</div>
<div>
<input type="text" v-model="filterValue" placeholder="Filter">
<button #click="invertSort()">Sort asc/desc</button>
</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
testData: [{name:'foo'}, {name:'bar'}, {name:'foobar'}, {name:'test'}],
filterValue: '',
sortAsc: true
};
},
computed: {
filteredAndSortedData() {
// Apply filter first
let result = this.testData;
if (this.filterValue) {
result = result.filter(item => item.name.includes(this.filterValue));
}
// Sort the remaining values
let ascDesc = this.sortAsc ? 1 : -1;
return result.sort((a, b) => ascDesc * a.name.localeCompare(b.name));
}
},
methods: {
invertSort() {
this.sortAsc = !this.sortAsc;
}
}
});
</script>

Related

how to get value of select<> & option<> tag in cypress

Is there any method or way to get the 'value' of <select><option> tag?
I have a scenario where i need to get the value of <select><option> tag, and store it in a variable because the value is dynamic, changing on every execution.
I cannot hard code that value like this, because it is changing every time:
cy.get(' ').select('b5c12d3-2085-4ed8-bd57-8a93f6ae1e64')
so i want to do something like this after getting that value:
cy.get(' ').select(value)
and by using text value, it is not selecting
cy.get(' ').select('related new) ....it is not working
You can use: cy.get('select option[value]').then($el => <logic to store values in Cypress.env>);
Markup:
<select>
<option>Select</option>
<option value="b5c12d3-2085-4ed8-bd57-8a93f6ae1e64">Some value</option>
<option value="more-such-dynamic-value">More value</option>
</select>
Test:
cy.get('select option[value]').then($options => {
return new Cypress.Promise((resolve, reject) => {
const values = [];
for (let idx = 0; idx < $options.length; idx++) {
values.push($options[idx].value);
}
if (values) {
resolve(values);
} else {
reject(null); // handle reject;
}
});
}).then((options) => {
Cypress.env('selectValues', options);
});
cy.log(`selectValues: ${Cypress.env('selectValues')}`);
cy.get('select').select('Some value').invoke('val').should('eq', Cypress.env('selectValues')[0]);
Cypress.env('selectValues', undefined); // clear
cy.log(`After reset, selectValues: ${Cypress.env('selectValues')}`);
Test Screenshot
You can use Cypress's invoke() for this, like:
cy.get('select option').each(($option) => {
cy.wrap($option).invoke('attr', 'value').then(($val) => {
console.log($val);
});
});
You could use the nth-child() selector to only grab one of the options:
cy.get('select option:nth-child(2)').invoke('attr', 'value').then(($val) => {
console.log($val);
});

vue-18n - how to force reload in computed function when changing language

I am using vue-i18n but I also have some content which is stored in database. I would like my text to be updated when the user changes the language.
I am using laravel and vuejs2.
Thanks in advance, I am not super familiar with vuejs yet. I hope it's clear enough.
in ContenuComponent.vue
<template>
<div>
{{$i18n.locale}} <== this changes well
<div v-html="textcontent"></div>
<div v-html="textcontent($i18n.locale)"></div> <== this won't work, I am wondering how to put params here (more like a general quetsion)
</div>
</template>
<script>
export default {
name:'contenu',
props: {
content: {
type: String,
default: '<div></div>'
}
},
computed: {
textcontent: function () {
console.log(navigator.language); <== this gives me the language as well, so i could use it if I can make it reload
var parsed = JSON.parse(this.content);
parsed.forEach(element => {
if(navigator.language == element['lang']){
return element['text'];
}
});
}
}
}
</script>
in ContentController
public function getcontent(){
$content = DB::connection('mysql')->select( DB::connection('mysql')->raw("
SELECT text, lang from content
"));
return view('myvue', ['content' => json_encode($content)]);
}
in content.blade.php
<div id="app">
<contenu content="{{ $content }}"></contenu>
</div>
You SHOULD NOT pass parameters to computed props! They are not methods and you should create method instead:
methods: {
textcontent () {
var parsed = JSON.parse(this.content)
parsed.forEach(element => {
if (navigator.language == element['lang']){
return element['text']
}
})
}
}
Also you should consider ES6 syntax:
methods: {
textcontent () {
var parsed = JSON.parse(this.content)
const content = parsed.find(element => navigator.language == element['lang'])
return content['text']
}
}
Much cleaner!
Please make sure to read about computed props and how they are different than methods or watchers: docs

syncError not flagged on Field instance with dot syntax name

I have a Field poll.id that is part of a form that contains multiple parts. Each part is contained in a component with a #reduxForm annotation and each component may have it's own unique validation method.
My problem is that in this case, when the validatePoll returns a validation exception, it doesn't show up at Field level. I wonder if this has to do with the dot syntax of the field.
#reduxForm({
form: 'posteditor',
destroyOnUnmount:false,
})
#autobind
class MyForm extends React.Component {
[...]
pollButton(field){
console.log('poll.id', field);//meta.error = undefined
[...]
}
poll(){
const { poll, visible, syncErrors } = this.props;
return (
<Field
name="poll.id"
value={poll && poll.id}
type="number"
component={this.pollButton}
props={{
visible: visible,
questions: (poll && poll.questions) || []
}}
/>
);
}
}
#reduxForm({
form: 'posteditor',
validate: validatePoll,
destroyOnUnmount:false,
})
#autobind
class PostPoll extends React.Component {
You left out your validation function, but my guess is that you are returning this:
validate(values) {
const errors = {}
if(!values.poll.id) {
errors['poll.id'] = 'Required' // ❌ 👎
}
return errors
}
...and you should be returning this:
validate(values) {
const errors = {}
if(!values.poll.id) {
errors.poll = { id: 'Required' } // ✅ 👍
}
return errors
}

Vue.js Component combined with Laravel Form Validation (i.e. passing an initial value for data)

After much Googling and finding the Vue.js forum down, I am ready to give up.
I'm creating a Postcode Lookup component, and everything was working well until I tried to combine it with Laravel's form validation - particularly when there's an error, and the form re-fills the old values.
Hopefully I cover everything here. I have a form input partial that I use which generates every form input. It also uses Laravel's old(...) value if present.
The issue is because there's a default value (in this case for postcode and address) of an empty string, this overrides the value attribute of Postcode input, and the content of the Address textarea.
In made up land, the ideal would be:
data : function() {
return {
postcode : old('postcode'),
address : old('address'),
addresses : [],
hasResponse : false,
selectedAddress : ''
};
},
So that's what I'm trying to replicate.
I can probably replace validation with Ajax validation, but my form partial changes the appearance of fields with an error slightly, so this would be messy
From my understanding:
I can't set an initial data value, as this will override the input value.
I can set a prop, but this is immutable
Any help I can find suggests 'using a computed property which determines its value from the prop' but if you literally do that, it doesn't update.
Here's what I have so far:
<so-postcode-lookup initial-postcode="{{ old('postcode') }}" initial-address="{{ old('address') }}"></so-postcode-lookup>
/**
* Allow user to select an address from those found in the postcode database
*/
Vue.component('so-postcode-lookup', {
name : 'so-postcode-lookup',
template : '#so-postcode-lookup-template',
props : ['initialPostcode', 'initialAddress'],
data : function() {
return {
postcode : '',
address : '',
addresses : [],
hasResponse : false,
selectedAddress : ''
};
},
computed : {
currentAddress : function() {
if (this.address !== '') {
return this.address;
} else {
return this.initialAddress;
}
},
currentPostcode : function() {
if (this.postcode !== '') {
return this.postcode;
} else {
return this.initialPostcode;
}
},
hasAddresses : function() {
return this.addresses.length;
},
isValidPostcode : function() {
return this.postcode !== '' && this.postcode.length > 4;
},
isInvalidPostcode : function() {
return !this.isValidPostcode;
}
},
methods : {
fetchAddresses : function() {
var resource = this.$resource(lang.ajax.apiPath + '/postcode-lookup{/postcode}');
var $vm = this;
var element = event.currentTarget;
// Fetch addresses from API
resource.get({ postcode : this.postcode }).then(function(response) {
response = response.body;
if (response.status == 'success') {
// Update addresses property, allowing select to be displayed
$vm.addresses = response.data;
} else {
$vm.addresses = [];
}
this.hasResponse = true;
});
},
setAddress : function() {
this.address = this.selectedAddress;
}
}
});
<template id="so-postcode-lookup-template">
<div class="row">
#include('partials.input', [
'label' => trans('register.form.postcode'),
'sub_type' => 'postcode',
'input_id' => 'postcode',
'autocorrect' => false,
'input_attributes' => 'v-model="currentPostcode"',
'suffix_button' => true,
'suffix_button_reactive' => trans('register.form.postcode_button_reactive'),
'suffix_text' => trans('register.form.postcode_button'),
'required' => true,
'columns' => 'col-med-50',
'wrapper' => 'postcode-wrapper'
])
<div class="col-med-50 form__item" v-show="hasResponse">
<label for="address-selector" class="form__label" v-show="hasAddresses">{{ trans('forms.select_address') }}</label>
<select id="address-selector" class="form__select" v-show="hasAddresses" v-model="selectedAddress" #change="setAddress">
<template v-for="address in addresses">
<option :value="address.value">#{{ address.text }}</option>
</template>
</select>
<so-alert type="error" allow-close="false" v-show="!hasAddresses">{{ trans('forms.no_addresses') }}</so-alert>
</div>
#include('partials.input', [
'label' => trans('register.form.address'),
'input_id' => 'address',
'type' => 'textarea',
'input_attributes' => 'v-model="currentAddress"',
'required' => true
])
</div>
</template>
If I try this, and set the model of the inputs to currentPostcode and currentAddress respectively, I seem to get an infinite loop.
I think I'm overthinking this somehow.
You can't bind directly to a prop but you can set an initial value using the prop and then bind to that, which is the way to go if you need a two way binding:
Vue.component('my-input', {
props: {
'init-postcode': {
default: ""
}
},
created() {
// copy postcode to data
this.postcode = this.initPostcode;
},
data() {
return {
postcode: ""
}
},
template: '<span><input type="text" v-model="postcode"> {{ postcode }}</span>'
});
Then just do:
<div id="app">
<my-input init-postcode="{{ old('postcode') }}"></my-input>
</div>
Here's the fiddle: https://jsfiddle.net/vL5nw95x/
If you are just trying to set the initial values, but don't need a two way binding, then you can reference the prop directly - as you won't be applying any changes - using v-bind:value:
Vue.component('my-input', {
props: {
'init-postcode': {
default: ""
}
},
template: '<span><input type="text" :value="initPostcode"> {{ postcode }}</span>'
});
And the markup:
Here's the fiddle: https://jsfiddle.net/pfdgq724/
Im working in a easy way to do that using laravel 5.4 controller to send the data directly
In Laravel view:
<input class="form-control" id="ciudad" name="ciudad" type="text" v-model="documento.ciudad" value="{{ old('ciudad', isset($documento->ciudad) ? $documento->ciudad : null) }}" >
in vue.js 2.0
data: {
documento: {
ciudad: $('#ciudad').val(),
},
},
In Laravel Controller
$documento = ReduJornada::where("id_documento",$id)->first();
return view('documentos.redujornada')->with(compact('documento'));

How do I keep context in react without stringing .bind(this)?

I'm using react to retrieve data from parse, manipulate it in my own function, and then update a component in the render.
The problem is that I can't update the state within my own, convoluted function unless I attach a string of bind(this). The entire component looks like this:
React.Component({
getInitialState: function () {
return{
isloading:true
}
},
componentDidMount: function(){
this.myStupidFunction()
},
myStupidFunction : function(){
(
(
(nested parse queries that eventually ...
return an object and set isloading:false).bind(this))
.bind(this))
.bind(this)
},
render: function (){
if (this.state.isloading) {
return(
<Text "...isloading"/>
)
} else {
return(
...actually return important stuff...
)
}
}
})
What is the smarter way to do this? Do I need to really .bind(this) for every nested function?
There are a few ways to maintain the context of your component.
Use ES6 Arrows
If you use ES6 arrows to define your functions. Arrow functions force the inner context of this to be the same as the outer context, regardless of how the function is called.
parse.find({
success: results => {
// this is correct
console.log(this);
}
});
I think this is the most elegant solution, but not all browsers support arrow functions yet.
Use Component Methods
React automatically binds this into each of the top level methods on your component. They are always guaranteed to have the correct context.
onSuccess: function() {
// this is correct
console.log(this);
},
componentWillMount: function() {
parse.find({
success: this.onSuccess
});
}
This is also fairly elegant, in my opinion. It lets React deal with the messiness of context whilst you just write code. However, it can mean that you end up with far too many methods at the top level of your component, so use it sparingly.
As an Argument
Some functions, such as map allow you to optionally pass a context to use as this as a final argument. This allows you to maintain the correct context without .bind(this).
data.map(function() {
console.log(this);
// this is correct
}, this);
This only works for some methods, so it's not really a universal solution.
Alias this
Create a reference to this and use that instead.
var __this__ = this;
parse.find({
success: results => {
// __this__ is correct
console.log(__this__);
}
});
This hack has been around forever in Javascript, but I don't think it's a great way to solve the problem.
Use ES7 Function Bind
For those who like to Javascript on the edge, you could also achieve this using the ES7 function bind syntax proposal — currently implemented in Babel.
parse.find({
success: this::function(results) {
// this is correct
console.log(this);
}
});
This requires using experimental proposal stage features of ES7. You may not want to start using it yet, but it's definitely interesting to be aware of. The value on the left hand side will be bound into the function on the right, as this.
Use a closure at the beginning of the function to capture this. It will be usable in any nested structure. The conventional names for such a closure are self _this and that. I prefer self.
myStupidFunction : function(){
var self = this;
someAsyncCall(1,2, function(result) {
//some nested stuff
anotherAsyncCall(1,2 function(innerResult) {
self.setState(innerResult);
});
});
}
one solution could be using local variable
myStupidFunction:function(){
var that=this
ParseReact.Mutation.Create('Place', {
name: 'New Place',
user: Parse.User.current()
})
.dispatch()
.then(function() {
that.refreshQueries();
});
}
Using ES7 Property Initalizer Syntax, currently implemented in Babel.
The key is the methodName = () => { //method return }
You can read more here.
import React from 'react';
export default class Note extends React.Component {
constructor(props) {
super(props);
this.state = {
editing : false
}
}
render() {
const editing = this.state.editing;
return (
<div>{ editing ? this.renderEdit() : this.renderTask() }</div>
)
}
renderEdit = () => {
return (
<input type="text"
className="edit-input"
autoFocus={true}
defaultValue={this.props.task}
onBlur={this.finishEdit}
onKeyPress={this.checkEnter} />
)
}
renderTask = () => {
const onDelete = this.props.onDelete;
return (
<div onClick={this.edit}>
<span className="task-body">{this.props.task}</span>
{ onDelete ? this.renderDelete() : null }
</div>
)
}
renderDelete = () => {
return (
<button className="delete-btn" onClick={this.props.onDelete}>x</button>
)
}
edit = () => {
this.setState({
editing : true
})
}
checkEnter = (e) => {
if(e.key === "Enter") {
this.finishEdit(e);
}
}
finishEdit = (e) => {
this.props.onEdit(e.target.value);
this.setState({
editing : false
})
}
}
// Note: Sample class from project above.

Resources