I'm trying to handle some star rating according to some data from an api. The problem is that I have created an array for the stars and nothing is rendered. If I console .log inside the fetchMovieDetails I can see there is data. What am I missing? I even tried var stars = useState([]) but still same result
export default function MovieDetails({ match }) {
const [movie, setMovie] = useState({});
var stars = [];
useEffect(() => {
fetchMovieDetails();
}, []);
async function fetchMovieDetails() {
// get data from api
// Handle star rating
var rating = (response.rating/ 2).toFixed(1);
for (var i = 0; i < 5; i++) {
var star = {};
if (i < ~~rating) star = 'filled';
else if (rating % 1 === 0) star = 'half-filled';
stars.push(star);
}
setMovie(response.data);
}
return (
<div className="movie-container">
<div className="movie-grid">
<div className="movie-rating">
<div className="star-rating">
// nothing is rendered here
{stars.map((star) => (
<span className="{`icon-star ${star}`}">
<span className="path1"></span>
<span className="path2"></span>
</span>
))}
{(movie.rating/ 2).toFixed(1)} / 5
</div>
</div>
</div>
</div>
);
}
I guest var stars is always re-declare. Why don't you using the useState like:
const [stars, setStarts] = useState([]);
useEffect(() => {
fetchMovieDetails();
}, []);
async function fetchMovieDetails() {
// get data from api
// Handle star rating
var rating = (response.rating/ 2).toFixed(1);
var stars = [];
for (var i = 0; i < 5; i++) {
var star = {};
if (i < ~~rating) star = 'filled';
else if (rating % 1 === 0) star = 'half-filled';
stars.push(star);
}
setStarts(stars);
setMovie(response.data);
}
If you prefer to not to keep stars in the state, and if you need to move the star logic in to a separate section then useMemo will help with it.
async function fetchMovieDetails() {
// get data from api
.....
setMovie(response.data);
}
const stars = useMemo(() => {
if (!movie.rating) {
//if movie is not fetched then return an empty array
return [];
}
const rating = (movie.rating/ 2).toFixed(1);
const starsTemp = [];
for (let i = 0; i < 5; i++) {
var star = {};
if (i < ~~rating) star = 'filled';
else if (rating % 1 === 0) star = 'half-filled';
starsTemp.push(star);
}
return starsTemp;
}, [movie]); //when movie gets changed call this function
Why i prefer this is, lets say you can add a rating by clicking on a star, and then lets say you need to show the updated rating, with your approach you have to change two states, rating property of movie and the stars, so basically if a one state is depending on a another state, its better to do like this.
Related
I have an array of objects with me:
Array of objects
Now, I want to change the property name of an particular object(not the value). For ex:
My object has attribute PRODUCT NAME:'BAJAJ Brahmi Amla Hair Oil 300 ml' in my data.
Required result:
PRODUCT_NAME:'BAJAJ Brahmi Amla Hair Oil 300 ml'
So, how can I replace the space in my property to an '_'.
Language used: React.js
I tried this function but it's not working for my desired result:
const productList = () => {
for (let i = 0; i < data.length; i++) {
let obj = data[i];
console.log(obj);
Object.keys(obj).forEach((key) => {
var replacedKey = key.trim().toUpperCase().replace(/\s\s+/g, "_");
if (key !== replacedKey) {
obj[replacedKey] = obj[key];
delete obj[key];
}
});
}
};
Can somebody explain why is this not working ?
can you please change replace(/\s\s+/g, "_") to replace(" ", "_") and then try, as you can see the below code
const productList = () => {
for (let i = 0; i < data.length; i++) {
let obj = data[i];
console.log(obj);
Object.keys(obj).forEach((key) => {
var replacedKey = key.trim().toUpperCase().replace(" ", "_");
if (key !== replacedKey) {
obj[replacedKey] = obj[key];
delete obj[key];
}
});
}
};
const string = "PRODUCT NAME"
console.log(string.trim().toUpperCase().replace(" ", "_"))
I'm still learning the basics of vue. I have different elements in one component, that have to be assigned in a second component. When I use console.log the array is shown correctly, but when i want to show the array in dynamically in template it is still the default value. What am I doing wrong?
Component one:
<template>
<div>
{{nr}}
{{containertyp}}
<button #click="click(0)"></button>
</div>
</template>
I have many more buttons with different parameters, just to make it shorter here.
export default: {
data: function {
return {
nr: [],
containertyp: [],
}
}
methods: {
click(number) {
for (var i = 0; i < 27; i++) {
this.nr[i] = false;
if (number == i) {
this.nr[i] = true;
}
};
EventBus.$emit('containerclicked');
}
},
created: function() {
let i;
//default
for (i = 0; i < 27; i++) {
this.nr[i] = false;
}
for (var index = 0; index < 27; index++) {
this.containertyp[index] = 'bs';
}
},
mounted() {
const self = this
EventBus.$on('containertypchosen', function (containertyp) {
for (let j = 0; j < 27; j++) {
if (self.nr[j] == true) {
self.containertyp[j] = containertyp
}
}
})
Component two:
<template>
<div>
<button :disabled = "disabled == true" v-on:click="chosetype('bs')" "> bs </button> <br />
</div>
</template>
export default: {
data: function() {
return {
disabled: true
}
}
mounted () {
const self = this
EventBus.$on('containerclicked', function (){
self.disabled = false
})
},
methods: {
chosetype: function (containertyp) {
this.containertyp = containertyp
EventBus.$emit('containertypchosen', containertyp)
}
}
}
You can't update arrays using indexes, the changes won't be detected by the reactivity system.
https://v2.vuejs.org/v2/guide/list.html#Caveats
So, for example, this won't work:
this.nr[i] = true;
Instead you'd need to use Vue.set, or the alias vm.$set:
this.$set(this.nr, i, true);
An alternative would be to create a new array and then replace this.nr entirely, i.e. this.nr = newArray.
You'll need to make a similar change everywhere that you're updating an array by index. There are updates to both nr and containertyp that currently have this problem.
It's not immediately clear from your code whether nr even needs to be an array. It seems that all the values are false apart from one, so you might be better off just holding the index of the true value instead of using an array.
I have a dynamic button being created. The problem is that the CurrentPage and the KeyIndex are already evaluated prior to rendering.
private DisplayButtons = () => {
let cont = [];
let startAt, endAt;
startAt = this.state.StartAt;
endAt = this.state.EndAt;
if (this.state.CurrentPage == 1) {
endAt = endAt =
this.state.TotalRecord / this.state.PageSize >= 5
? 5
: Math.ceil(this.state.TotalRecord / this.state.PageSize);
}
for (var i = startAt; i <= endAt; i++) {
cont.push(
<li onClick={this.onPagerClick} data-id={i} className="sui-pager-element">
<a
className={
this.state.CurrentPage == this.state.KeyIndex
? 'sui-selected sui-pager-element'
: 'sui-pager-element'
}
>
{i}
</a>
</li>,
);
}
this.setState({
DataButtons: cont,
});
};
It is difficult to understand this isolated code. However, I make some comments that I hope will improve its functioning.
I understand that DisplayButtons is an internal function of a class component. If that is the case, it should be called displayButtons with first lower case.
You can refactor like this:
const { startAt, CurrentPage, TotalRecord, PageSize, KeyIndex } = this.state;`
let { endAt } = this.state;
Better use FP in the for loop. It is not very clear.
cont should be an array of objects instead of html. Then, in the render, you do something like this: { DataButtons.map(b => <ButtonView>) }.
I have a simple AngularJs application of medical cards.
I have storage with it and display it at my home.html using dx-datagrid:
One card has many records, I get records of card from recordsArray by cardId
getVardsRecordsByCardId: function (id, recordsArray) {
if (recordsArray.length != 0) {
for (var i = 0; i < recordsArray.length; i++) {
if (recordsArray[i].cardId === id) {
cardsRecords = cardsRecords.concat(recordsArray[i]);
}
}
}
return cardsRecords;
}
Now I have records just in the third card. I added a function on button for testing it:
var jdskla = [];
var localCardId = 0;
$scope.showCardDetails = {
text: "",
type: "default",
icon: "preferences",
onClick: function () {
if ($scope.itemIdFromShowButton) {
$location.path('/carddetail/' + $scope.itemIdFromShowButton);
var jdskla =[];
var jdskla = businessLogicOfMyApp.getVardsRecordsByCardId($scope.itemIdFromShowButton, $scope.recordsArray);
console.log($scope.itemIdFromShowButton)
console.log(jdskla);
}
else {
alert("Error!!!");
}
}
};
1,3,1 is cardId's and array of records. But, why array of card records don't clears and save last data?
May be somebody know how I can resolve it? Thanks for your answers!
P.S. I'm using ng-view directive in my app and i tried to clear my array use another button:
$scope.backToGeneralPage = {
text: "Back",
onClick: function () {
jdskla = [];
$location.path('/');
}
};
but it wasn't helpful.
You should initialize cardsRecords array in function getVardsRecordsByCardId.
getVardsRecordsByCardId: function (id, recordsArray) {
var cardsRecords = []; // initialize array locally
if (recordsArray.length != 0) {
for (var i = 0; i < recordsArray.length; i++) {
if (recordsArray[i].cardId === id) {
cardsRecords.push(recordsArray[i]);
}
}
}
return cardsRecords;
}
In my controller I have code that filters just fine but i want to create a new field that concatenates two fields for an Angular filter in html. This is what I have that doesn't work.. Is this possible?
private filterByColumns: string = "";
getData = (): void => {
var vm = this;
this.carHopService.getDetails({ id: this.$state.params["id"], type: this.$state.params["type"] }).then(
(data: any) => {
vm.primaryCarHopData = _.filter(data.carHopList, {
carHopType: "Primary"
});
---> **vm.primaryCarHopData = _.map(data.carHopList, {
vm.filterByColumns=fullName + " " + age
});**
});
};
That's not how map works. In the callback function, you need to return something:
_.map([0,1,2], (x) => x + 1)
> [1,2,3]
// old syntax
_.map([0,1,2], function (x) { return x + 1 })
> [1,2,3]
You can simply replace _.map with _.forEach and you will have your mapped data in data.carHopList.
Clarification:
I'm not good with words so let me put here very simple implementations for both forEach and map:
// these functions do not mock lodash counterparts 100%
// as lodash fns can work with objects too
// and they have some shortcuts, see docs
function forEach(arr, callback) {
for(var i = 0; i < arr.length; i++) {
callback(arr[i], i);
}
return arr;
}
function map(arr, callback) {
var newArr = [];
for(var i = 0; i < arr.length; i++) {
newArr[i] = callback(arr[i], i);
}
return newArr;
}
Someone had steered me in the direction of map. What I ended up doing was using Angular by adding this to controller:
filterTextByCols = (row) => {
return (angular.lowercase(row.fullName).indexOf(angular.lowercase(this.filterQuery) || '') !== -1 ||
angular.lowercase(row.birthDate).indexOf(angular.lowercase(this.filterQuery) || '') !== -1);
}
And then using filterTextByCols in filter:
<div ng-repeat="person in vm.persons | orderBy:sortType:sortReverse | filter: vm.filterTextByCols">