v-select with dynamic item list - vuetify.js

I have a v-select (don't forget the return-object or you won't get the ID):
<v-select v-model="selectedEmployee"
:items="employee"
item-text="name"
value="id"
return-object
hide-details
></v-select>
I declare the array in data plus a helper array and a variable:
data: {
employeeList: [],
employee: [],
selectedEmployee: null
},
I get the JSON array with a GET and put it into employeeList and then I wrangle the data to be displayed in the v-select:
for(i = 0; i < app.employeeList.length; i++) {
app.employee.push({name: '', id: ''});
app.employee[i].name = app.employee[i].lastname + ' ' + app.employeeList[i].firstname;
app.employee[i].id = app.employeeList[i].id;
}
The employee.name is shown in the v-select and because of return-object you get the .id along with it. So the content of selectedEmployee is:
{name: 'Jane Doe', id: 0}
If you don't use return-object you only get what is shown in the v-select dropdown menu.

Showed you above how to make a dynamic item list for v-select and how to get a workable value out of it.
Took awhile to piece it together thanks to the bad / inaccessible documentation.
Maybe it's useful for somebody.

Related

Cypress - verify if each table row in one column contains the same item

I have a table, however some kind of ag-grid, created by DIV's, not real table element:
<div role="row" row-index="1" >
<div col-id="name">Name 1</div>
<div col-id="age">25</div>
</div>
<div role="row" row-index="2" >
<div col-id="name">Name 1</div>
<div col-id="age">25</div>
</div>
I want to verify, if EACH field with col-id="name" contains the same item. I am testing kind of filtering, so if user filters the Name 1, I want to check each row, if there is only Name 1 and nothing else.
As each field which I need to check has the same col-id, I tried it this way:
cy.get('div[col-id="name"]').contains('Name 1')
or
cy.get('div[col-id="name"]').should('contain','Name 1')
but both cases passes even if some field contains another name, because it finds at least one match.
How to assert that each field with col-id="name" should contain ONLY Name 1 ?
Maybe use .each() to test the elements individually
cy.get('div[col-id="name"]')
.each($el => {
expect($el.text()).to.eq('Name 1')
})
Without .each()
cy.get('div[col-id="name"]')
.should($els => {
const names = [...$els].map(el => el.innerText)
expect(names.every(name => name === 'Name 1').to.eq(true)
})
You can verify the combined text of all elements
cy.get('div[col-id="name"]')
.invoke('text')
.should('eq', 'Name 1'.repeat(2)) // assuming two elements, equals "Name 1Name1"
or, this is better for asynchronous fetching of name
cy.get('div[col-id="name"]')
.should('have.text', 'Name 1'.repeat(2)) // equals "Name 1Name1"
When element count is unknown
cy.get('div[col-id="name"]')
.then($els => {
const count = $els.length
cy.wrap($els).should('have.text', 'Name 1'.repeat(count))
})
In case you don't want to apply assertion and just print out statements stating whether the element was found/not found, you can do like this:
cy.get('div[col-id="name"]').each(($ele, index) => {
if ($ele.text().trim() == 'Name 1') {
cy.log('Item Found at position ' + index)
} else {
cy.log(
'Expected Item Not found at position ' +
index +
'. Instead the item was ' +
$ele.text().trim()
)
}
})

populate cards and modals via same json file using react(-bootstrap)?

The data from workData fills <Card></Card> correctly.
The <Modal></Modal> only fills with the last entry of workData (e.g. Test4, Modal4, test text 4...)
my goal is to generate cards and respective modals (for each card) using the data from the json, in the same file.
why is the modal only being filled by the last properties in the json? how do i get it to populate with the entire array? if possible please explain why this does not work the way it is.
if it's not obvious im super new, i am, any responses would be super appreciated. ty
cards good
after clicking "Read1" bad, should say Test1, test text 1
in App.js: import { Works } from "./Works";
in Works.js: import { workData } from "./data";
also in Work.js:
export const Works = () => {
const [show, setShow] = React.useState(false);
const onClick = () => setShow(true);
return (
<>
<div className="work-container">
<Row xs={1} md={2} lg={4} className="g-4">
{workData.map((data, key) => {
return (
<div key={key}>
<Col>
<Card>
<Card.Img variant="top" src={data.projectImage} />
<Card.Body>
<Card.Title>{data.projectTitle}</Card.Title>
<Card.Text>with {data.projectTeam}</Card.Text>
<Button variant="link" onClick={onClick}>
{data.readMore}
</Button>
</Card.Body>
<Card.Footer>{data.tags}</Card.Footer>
</Card>
</Col>
<Modal
show={show}
onHide={() => setShow(false)}
dialogClassName="modal-95w"
>
<Modal.Header closeButton>
<Modal.Title>{data.projectTitle}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Image src={data.projectImage}></Image>
<p>
{data.modalText}
</p>
<Image src={data.modalImage}></Image>
</Modal.Body>
</Modal>
</div>
);
})}
</Row>
</div>
</>
);
}
in data.js:
export const workData = [
{
projectTitle: "Test1",
modalTitle: "Modal1",
modalText: "test text 1",
modalImage: "image",
readMore: "Read1",
projectImage: "image",
projectTeam: "Test1",
year: "2022",
link1: "link",
link2: "link2",
tags: [
"#tag1 ",
"#tag2 "
]
},
...
The data from workData fills <Card></Card> correctly.
The <Modal></Modal> only fills with the last entry of workData (e.g. Test4, Modal4, test text 4...)
my goal is to generate cards and respective modals (for each card) using the data from the json, in the same file.
why is the modal only being filled by the last properties in the json? how do i get it to populate with the entire array? if possible please explain why this does not work the way it is.
cards good
after clicking "Read1" bad, should say Test1, test text 1
You iterate over workData for Cards and Modals, but you use only one single state for everything. What you need to do, is to also create a state for every Modal. Usually you create an array with unique id as key and boolean value. I assumed projectTitle is unique:
{
Test1: false,
Test2: false,
Test3: false
}
Because you don't know the length of your data, you just iterate over the array, as you have done for Cards und Modals:
const initialShowState = Object.fromEntries(
workData.map((data) => [data.projectTitle, false])
);
const [show, setShow] = React.useState(initialShowState);
Then you need to create a generic callback function, which takes the id of the Card and shows the appropriate Modal. I simplified the logic and created a toggle function:
const toggleShow = (id) =>
setShow((prev) => {
return { ...prev, [id]: !prev[id] };
});
Finally, when you render UI and iterate over workData, you need to apply the callback function to Button onClick and Modal onHide event handlers and set the show property of Modal:
<Button variant="link" onClick={() => toggleShow(data.projectTitle)}>
...
<Modal
show={show[data.projectTitle]}
onHide={() => toggleShow(data.projectTitle)}
dialogClassName="modal-95w"
>
That's it. Here is the working sandbox: https://codesandbox.io/s/hungry-sunset-t865t3
Some general tips:
You don't need the outer Fragment in Works as you only have one upper most element
If you use JSX syntax in your file, your extension should be .jsx and not.js (Works.jsx)
Using index as key in the list is bad practice. Find some unique id in your data

Use methods and computed properties in child component

In my List component I have a method which count the length of the array within certain categories.
methods: {
getLengthofaCategory(cat) {
const LowerCaseSearch = this.search.toLowerCase();
let categoryCount = this.products.filter(
product =>
(product.name.toLowerCase().includes(LowerCaseSearch) ||
product.category.toLowerCase().includes(LowerCaseSearch)) &&
(!this.checked.length || this.checked.includes(product.category)) &&
product.category === cat
);
return categoryCount.length;
}
}
See here my setup in this sandbox.
But I want the values next to the checkboxes (which are coming from my CheckBox component).
How do I get the logic from the method getLengthofaCategory into my CheckBox component?
So I am able to use {{ getLengthofaCategory('tennis') }} in the v-for loop, inside the CheckBox component. And then maybe I can also use category.value instead of hardcoding e.g 'tennis' as the paramater?
In your list.vue, you can use the already created computed function filteredData instead of doing the filter again. This saves some performance because in Vue, computed properties are "cached" once run.
So you can create a new computed function that creates an object with keys per category and value can either be just the amount or an array of products in this category.
I would then pass this computed value to the CheckBox component via a prop, then inside the CheckBox component, you can display the .length or value regarding how many items each category has:
List.vue:
computed: {
//...
amountPerCategory() {
return this.filteredData.reduce((categories, product) => {
if (!(product.category in categories)) {
categories[product.category] = [];
}
categories[product.category].push(product);
return categories;
}, {});
}
}
CheckBox.vue:
<span class="ml-2 text-gray-700 capitalize">{{ category.value }}</span> -
<span
v-if="count[category.value]"
class="ml-2 text-gray-700 capitalize"
>{{ count[category.value].length }}</span>
count: {
type: Object,
default: () => ({})
}
https://codesandbox.io/s/admiring-ellis-4hojl?file=/src/components/CheckBox.vue

Update smart table after content changed

I have an array of objects, which I display using st-table directive.
I filter the table by a value of a certain field in the objects.
The problem is, once a value of a field in these objects has been changed, the filtering is not performed.
I believe the reason for it is that smart-table watches the array's length, but doesn't perform deep comparison to see whether or not the values inside any of the objects changed.
What can I do to solve this?
edit: added code:
angular.module('myApp', ['smart-table'])
.controller('mainCtrl', ['$scope', '$timeout',
function ($scope, $timeout) {
$scope.rowCollection = [
{
name: "sth odd",
number: 1
},
{
name: "sth even",
number: 1
}
];
$scope.displayedCollection = [].concat($scope.rowCollection);
function changeNumber(){
$timeout(function(){
$scope.rowCollection[1].number = $scope.rowCollection[1].number === 1 ? 2 : 1;
changeNumber();
}, 1000);
}
changeNumber();
}
]);
http://plnkr.co/edit/IVYy5WrsiEJSRXZCqY9z?p=preview
Notice how when you search e.g number "2", the view isn't updated even though the property of the second item sometimes is "2" and sometimes not.
Found a solution for you, instead of using st-search use a plain ng-model and then filter by the ng-model value. then in the ng-repeat filter by that value
so instead of this
<input st-search="number" placeholder="search for number" class="input-sm form-control" type="search"/>
...
<tr ng-repeat="row in displayedCollection">
<td>{{row.name}}</td>
<td>{{row.number}}</td>
</tr>
do this
<input ng-model="searchMe" placeholder="search for number" class="input-sm form-control" type="search"/>
....
<tr ng-repeat="row in filterd = (displayedCollection | filter: searchMe)">
<td>{{row.name}}</td>
<td>{{row.number}}</td>
</tr>
here's a plunker, enter 2 and see how it redos the filtering each time
To refresh the smart-table you can add or remove items as you know or use a new array.. so just recreate the array.
$scope.rowCollection = [].concat($scope.rowCollection);

Knockout.js ObservableArray sort not updating UI

I have a Knockout observable array that refuses to update the UI (a jquery Accordion) to which it is bound after a .sort() call, but happily updates the UI after a .reverse() call - I've been banging my head against this problem for days - can't seem to find an answer... help!
Container View Model - implements the observableArray Property :
function DataTextsKOViewModel( jsonTexts ) {
var self = this;
// Texts array
self.texts = ko.observableArray();
// Build from json data array
for (var i = 0; i < jsonTexts.AuthorityTexts.length; i++) {
var jsontext = jsonTexts.AuthorityTexts[i];
// Push Text VModel objects onto the KO observable array list ..
self.texts.push(
new DataTextKOViewModel( jsontext )
);
}
}
Array Object Model - These are the objects which are sorted:
// Single data text view model
function DataTextKOViewModel(jsonText) {
// Other properties omitted for brevity
this.ListOrder = ko.observable(jsonText.ListOrder);
}
Custom Sort Function :
function textListOrderCompare(l, r) {
// Ascending sort
var retval = ((l.ListOrder() == r.ListOrder()) ? 0
: ((l.ListOrder() > r.ListOrder()) ? 1 : -1));
return retval;
}
Binding Markup :
<!-- ko foreach: texts -->
<div class="group" data-bind="attr:{'id': clientId() }">
<h3 data-bind="attr:{'dataKey': responsibilityId() }">
<span data-bind="text: responsibilitySectionHeader"></span>
</h3>
<div>
<!-- section content goes here -->
</div>
</div>
When User Elects to sort:
myDataTextsKOViewModel.rollbackChanges();
dataTextsViewModel.texts.sort(textListOrderCompare);
// UI Never updates - but I've checked console output to ensure that the array
/ /sorts correctly
dataTextsViewModel.texts.sort(textListOrderCompare);
// UI updates with items in reverse order
Any help you can give me would be most appreciated.
EDIT: Should have mentioned - I already call valueHasMutated() on the observable array property after the .sort()! - Andrew
Try to call valueHasMutated function after sorting:
dataTextsViewModel.texts.sort(textListOrderCompare);
dataTextsViewModel.texts.valueHasMutated();
dataTextsViewModel.texts(dataTextsViewModel.texts().sort(textListOrderCompare));
this way you change observable. Other way was changing the array inside and didn't trigger the change.

Resources