Strapi CMS: Add calculated field - javascript

I am trying to use and learn Strapi Headless CMS while implementing it in a small company. I need to calculate some fields and display it in the form (while filling fields) and table.
I was looking in the model life cycle but I did not find any cicle regarding input changes, just the model.
I have tried beforeSave cycle but it is obviously triggered after an user clicks on Save button, but according to their own documentation should work:
beforeSave: async (model, attrs, options) => {
model.set('FinalCost', attrs.budget- attrs.cost);
}
This code doesn't work, but I am trying to show how the finalCost field, after filling cost and budget should look like (in real time). I also tried attrs.FinalCost = attrs.budget - attrs.cost but nothing changes.
Any clues? Thanks in advance.
Edit:
I had to verify that budget field were truthy before setting FinalCost:
beforeSave: async (model, attrs, options) => {
if (attrs.FinalCost) {
attrs.FinalCost = attrs.budget- attrs.cost;
}
}
But this does not answer my first issue, that this should work in real time and bot until I press "Save" button.

Here are some resources that will help you.
Model's life cycles function are called when an entry is created/update/...
So in your case FinalCost is an attribute of your model and its value will be updated and saved any times your update your entry.
This is the same system as in this guide - https://docs-v3.strapi.io/developer-docs/latest/guides/slug.html
If you don't want to store the value is a field, you will have to update the API controller to calculate the value on the fly.
That is done in this guide - https://docs-v3.strapi.io/developer-docs/latest/guides/custom-data-response.html

Related

How to update a specific field of data only one time for each user in firesrore in firebase v9?

I am creating a sns-like web application, and trying to implement a "liking a post" function. Basically each post has data in firestore, such as the caption of the post, the image of the post, number of likes on the post and etc. What I want to do is, if a user clicks a like button on any post, the number of likes on the post clicked will be incremented by 1. However, I do not know how I can restricting each user can like only one time on each post. My current code works for incrementing the number of likes but the user can like a post as many times as the user clicks.
I am totally a beginner in firebase, react and javascript and would be really appreciated for any help. Thank you.
here is my code working for incrementing a number of likes.
const likeHandle = async () => {
const docRef = doc(db, "posts", postId);
await updateDoc(docRef, {
noLikes: noLikes + 1
});
}
You won't be able to do this at scale inside the same document where you are tracking the number of likes, because there is a max size to each document.
What you will have to do instead is store a record of each pair of user and like in its own document in a separate collection, and check to make sure that any new like requests are not already represented in that other collection. Your UI should also probably disable likes if there is a record for that pair of user and post so that the user is not presented with an option that you don't want them to take.
There is no direct way to limit how often a user can write a specific piece of data.
What you can do however is:
Add a usersWhoVoted field with UIDs to the document, or a userWhoVoted subcollection under it.
Write both the increased number of likes and the UID to the database in one operation (using a transaction/batched write if you chose a subcollection).
Use security rules that only allow the user to increment the count if they also added their UID in that operation, and only decrease the count if they removed it (if that is also a use-case you want to support).
You should probably also use the atomic increment operation, to prevent depending on client-side state that can be outdated (although your security rules should catch and reject that situation anyway).

Meteor react design - nested component: function call

I'm currently facing a problem with Meteor and React, where i know some partly solutions but they don't work and imo none of them is pointing in the true direction.
The situation:
All is about an fitness app: I have a structure that represents exercises for customers, while each exercise can have a defined number of sets (a set is how often a exercise should be done). Each set has some properties (all the user can manipulate within the font-end).
Now i have the following component structure with some map-functions (state properties are in {}):
Training {customers,exercises,datetime,otherinfos}
- Overview {customers,exercises}
exercises.map():
- Exercise {exercise,customers}
customers.map():
- Customer {exercise,customer}
exercise.sets.map()
Set {exercise, customer, set, valuesofset}
From a UI-perspective (react) this all works without problems.
Now the idea is to have a button "Save" within the Training component. When the button is pressed, I want to save the state of all Set-Components in a "sets" collection (if it has other values than the default placeholder ones) and at the same time save the Training-Component in a "trainings" collection. But the training should also include information about what Sets are integrated (so at least the Set._id should be in the Training-Component state at time of Saving.
Here now my ideas so far:
Create refs from Training all the way down to all Sets and then, when pressing "Save" iterate over all refs and call a "Mongo.insert" from all Sets. Here i have the problem that i cannot return the inserted _id. Of course i could call a different function in each Component from Set all the way back to Training, but imo this is an overflow.
Try to manage the state of all sets within the Training state by calling a nested function. As i have onChangeHandler on the Inputs, this would always call a method in Training and check which one of the Sets was changed and then changes it. I have tried it this way, but it led to a very bad performance.
Create a temp-ID for Training, forward it to to the Sets (using the componentWillReceiveProps method) and when in Set, insert the Set in the database with the temp-ID. Then receive all Sets with temp-ID and use it to add the Training in the database. --> imo very complicated and I don't really want to do a database call if it is not necessary.
So currently i don't know how to solve this problem. The reason i try to separate "sets" and "trainings" is given through the fact, that later on i would like to give information about the last Set right next to the new empty Set whenever one is on the database. Any tips are welcome!
EDIT:
As suggested, there is also the possibility to solve the problem with Session. So therefor i have added the following code to Set:
componentDidMount() {
Tracker.autorun(() => {
Session.set(`set_${this.state.id}`, {
...this.state
});
});
}
My idea was then to iterate over all Session-Keys from Training which start with "set_" - unfortunately there is no function to that holds all Keys.
Second idea was to to use an array as value for a Session-pair. However, it's quite a procedure to handle the update of the reactive Set component (copy array from session, check whether an element is available or not, create a new one or update the existing one).
EDIT2:
I think i got a solution with Session:
Object.getOwnPropertyNames(Session.keys)
did the trick to get all SessionKeys! Thank you for your help!
If you do not want to use Redux or pass parent bound callbacks in the child component, you can try Session to store data at app level which can be accessed(set/get) in any component
https://docs.meteor.com/api/session.html
In your case, you may set values of "Set" in Session and access it in Training. You may also need https://guide.meteor.com/react.html#using-withTracker. Using withTracker will help in doing reactive update of the database on change of any Session variable.

Apply model changes immediately

I've form with some input fields, upon submit I make a $http call and populate a model object that binds data to a table on my view. Once user saw the result, they may change the data in the form and submit the same once again, I'm trying to clear the result and populate the data with the new result. Some time the result can be same, in such cases user is not able to understand whether the form was submitted and new results has been updated or not. I'm setting my model object to undefined expecting that the data on the table will get wiped. I also have an ng-if for null check of the object and control the display of the table, I expect it to hide the table, but unfortunately it is not happening. How can I do that? $scope.apply() is the only way for me to do that. I hate to use it as it causes full digest cycle. Please advise.
You can create a separate scope for clearing the data and call that using model on button click.
Example
$scope.clearData = function () {
$scope.ApplicationFormName = "";
$scope.FirstName="";
//put your logic for clearing the form here
};
in HTML
<button title="Clear" ng-click="clearData()"></button>

Model in from ng-option not displaying results | Possible dynamic model?

I currently list a set of options for ng-options from a resource that contains JSON.
[
{
id:23,
name:"Other"
},
{
id:24,
name:"Crew"
},
{
id:25,
name:"Announcer"
},
{
id:26,
name:"Producer"
},
{
id:27,
name:"Cameraman"
},
{
id:28,
name:"Monitor"
}
]
This is all added into my scope at $scope.broadcaster = response.data. I then loop through everything in my options by a simple ng-options.
<select ng-model="selectedrole" ng-options="roles as roles.name for roles in broadcaster" ng-init="selectedrole=broadcaster[0]">
</select>
Everything goes good once the page loads up. I can select from my list of names and the ng-init starts on the first selection as anticipated. However I have a few issues that I can't seem to resolve.
1) In my doc I set up {{selectedrole}} and I expected to see the contents of my model on the page reflected by my current selection. Instead I see no output at all. I can't tell if the model is even being updated properly so I'm not sure if I can use it in my formdata.
2) This select is generated on click so users can select a role for more than one person. However since the model is the same, any selection is copied over which ever one is changed. I need to find a way to output the model to check the data, but make that model dynamic so i can get a list of results from all the different selections.
EDIT:
Now this is really crazy. I mocked it up in a plunker and its at least showing the model just fine. Are there some conflictions I should worry about that would stop the model from updating? I literally copy pasted my data and its working in plunker but not my project.
http://plnkr.co/edit/GbdOmdjj1M3OuZKO5cSq?p=preview
EDIT2:
Even more info. As I stated before, the select is created when a user clicks on a function that creates an ng-repeat with user information and a new select option for role. If I place the {{selectedrole}} within that ng-repeat I actually get all the data returned when I user selects it. It seems that since the click creates a push of new data, it will not work outside each item. The issues now is that every instance has its own model so i need to figure out how to gather all this data from each ng-repeat and post it to the form. I'll try to update as I work through the issue.

Rails: Using input from dropdown menu without posting

I'm pretty new to Rails and web dev in general. I need to display two dropdown menus, states and schools, such that schools is only displayed after the user has chosen the state, and schools should only display the schools in the chosen state. What I don't know is how I can use the states choice to decide dynamically what schools to display, without the user having to click Submit. I understand that I may need to use JavaScript, but not knowing JS well, I'm not really sure how to do that. Hope I'm making sense. Thanks!
Here is a simple example of dynamically populating a select based on data structures already in your JavaScript. If you need to perform a server request after the user selects a state and return the list of schools, you'll need different code (and helpfully a library like jQuery).
I think you want to do this with AJAX. I'm not going to customize this for Rails 1 but you should be able to follow the idea. Your first dropdown has a list of states, and each state has a list of schools.
// some js file that's loaded from your layout
// When your states dropdown is changed it fires an ajax call
var success = function(response) {
for (var school in response.schools) {
$('#schools_dropdown').html('');
var option = $(document.createElement('option')).html(school.name).val(school.id);
option.appendTo($('#schools_dropdown'));
}
});
$('#states_dropdown').change(function() {
$.get('/state/' + $(this).val() + '/schools', success);
});
# your schools controller
def index
#schools = State.find(params[:id]).schools
respond_to do |format|
format.js { render :json => #schools }
end
end
So maybe you don't have jQuery and maybe rendering json is different in Rails 1... but the idea is the same. You have some javascript attached to your states dropdown so that when it changes, you pull off the id of that state and make an AJAX call to your controller. The last parameter to that AJAX call is a success function that loops through all the schools sent back by the controller, clears the schools dropdown, and adds options into the dropdown one by one.

Categories