I need help with searching the meteor collection with more parameters.
I am using search query and filters to see certain objects from a collection. The problem is that I want client to load whole collection and then reactively change what the user sees, but only changing the subscribe, not calling server again. Thill now search query + one filter is working okay, but only if I call server every time something changes. Now in my code below you can see that I am doing it with if else elements, but that is not a good way. Any suggestion will help. Thank you.
Template.jobs.onCreated( function showOnCreate() {
Meteor.subscribe('Jobs');
this.searchQuery = new ReactiveVar('');
this.remoteQuery = new ReactiveVar(false);
this.typeQuery = new ReactiveVar(false);
});
Template.jobs.helpers({
job: () => {
query = Template.instance().searchQuery.get();
remoteQuery = Template.instance().remoteQuery.get();
typeQuery = Template.instance().typeQuery.get();
let regex = new RegExp( query, 'i' );
// **************************
// the problem starts here
// **************************
if (Router.current().params.slug) {
const companyJobs = Company.findOne({slug: Router.current().params.slug}).jobs;
if ( companyJobs !== undefined) {
return Meteor.subscribe('Jobs', {'_id': { '$in': companyJobs }});
}
return false
} else if (Router.current().params.slug === undefined && remoteQuery === true ) {
return Job.find({ $or: [ { Name: regex }, { Description: regex }, ] , Remote: true, positionType: [],});
} else if (typeQuery = '') {
return Job.find({ $or: [ { Name: regex }, { Description: regex }, ] , positionType: typeQuery, });
},
// -------*****************************
employer: () => {
if (Router.current().params.slug === undefined) {
Meteor.subscribe('Companies');
return 'Poslodavac: ' + Company.findOne({_id: Template.currentData().CompanyId}).Name;
}
return false
},
jobUrl: () => {
Meteor.subscribe('Companies');
companySlug = Company.findOne({_id: Template.currentData().CompanyId}).slug;
return ('/company/' + companySlug + '/job/' );
}
});
Template.jobs.events({
'click .positionType': (event, templateInstance) => {
if (Template.instance().remoteQuery.get().lenght > 1){
Template.instance().typeQuery.set(Template.instance().remoteQuery.get().push(event.target.value));
console.log(Template.instance().remoteQuery.get())
} else {
console.log(Template.instance().remoteQuery.get())
console.log('ggggggg')
Template.instance().typeQuery.set(event.target.value);
}
},
'click #remoteFriendly': (event, templateInstance) => {
Template.instance().remoteQuery.set(!Template.instance().remoteQuery.get());
},
});
Html tempalte with filters:
<template name="jobs" >
<div>
<p>Filteri:</p>
<span>
<input type="checkbox" id="remoteFriendly" name="remote"> <span for="remoteFriendly"> Remote friendly? </span>
</span>
<span>
<p>Tip pozicije:</p>
<input type="checkbox" class="positionType" id="1" value="Programiranje" > <span for="1"> Programiranje </span>
<input type="checkbox" class="positionType" id="2" value="Dizajn" > <span for="2"> Dizajn </span>
<input type="checkbox" class="positionType" id="3" value="Marketing" > <span for="3"> Marketing </span>
<input type="checkbox" class="positionType" id="4" value="Ostalo" > <span for="4"> Ostalo </span>
</span>
</div>
{{#each job}}
<div style="border: 0.1rem solid black; margin: 1cm; padding: 5px; max-width: 420px;" > <!-- OVO JE PRIVREMENI STIL, OBRISATI-->
<p> Posao: {{Name}} <br> Opis: {{Description}}</p>
<p> {{employer}} </p>
<p>Remote friendly?: {{Remote}}</p>
<p>Tip pozicije: {{positionType}}</p>
<p> Saznajte vise OVDE</p>
</div>
{{/each}}
<p id="nesto"></p>
</template>
Welcome to SO!
You seem to be confused between Pub/Sub and Collection.find.
You should first realize that the 2 are different mechanisms, which provide different functionalities.
Pub/Sub indeed sends data from your Server into your Client's Minimongo database. But this data is not displayed yet.
Collection.find is used on your Server against your actual MongoDB, and on your Client against your local Minimongo DB.
Therefore on your client, once you have correctly subscribed to your server publication (typically at app level or template level / in onCreated hook), you can directly call Jobs.find in your helpers (or anywhere else) to get your documents, without having to change the subscription (unless the latter needs new parameters).
There should be nothing wrong with your commented code:
return Job.find({'_id': { '$in': companyJobs }});
In general, avoid any expensive computation in helpers (like Meteor.subscribe), as helpers may be executed many times without you noticing it. Your Meteor.subscribe('Companies') should also go to template level (i.e. in onCreated hook).
Therefore, instead of doing your if / else conditions in your helper, simply do it once at your template level. To account for your need to use a value from another document in another collection, why not just passing directly the company's slug as an argument to your Jobs subscription, and performing the computation Server-side? Or even just subscribing to everything, as your current initial subscription seems to do.
Then your helper will just use Jobs.find, which queries against your Client's local minimongo DB, leaving your Server unbothered.
Related
JS 13 and inside my ReadMoreButton client component i push my article data using useRouter hook of NEXT.
Not i can not use useRouter hook inside NEXT.JS server component so here i fetch searchParams and fetch that data.
here problem is before rendering i am checking if searchParams are defined or not not if i check in development everything work fine it render data but in production mode it show page not found error even if data is correctly send.
when i run next build it give me following output Output
and i am running side in production mode using next start and it show page not found when i do /article?serchParamsData.
You can check my whole code here : https://github.com/ssiwach8888/Next.JS-News-App
i also deploy production build on Vercel but it also show same error.
I am using NEXT.JS 13 with typescript
# ReadMoreButton.tsx "First Control goes here."
"use client";
type Props = {
article: NewsData;
};
import { useRouter } from "next/navigation";
//For navigate to SSC
const ReadMoreButton = ({ article }: Props) => {
const router = useRouter();
const handleClick = () => {
const queryString = Object.entries(article)
.map(([key, value]) => `${key}=${value}`)
.join("&");
const url = `/article?${queryString}`;
router.push(url);
};
return (
<button
className="bg-orange-400 h-10 rounded-b-lg dark:text-gray-900 hover:bg-orange-500"
onClick={handleClick}
>
Read More
</button>
);
};
export default ReadMoreButton;
# Article.tsx "Then we navigate to this page."
type Props = {
searchParams?: NewsData;
};
import { notFound } from "next/navigation";
import LiveTimestamp from "../Components/LiveTimestamp";
import Link from "next/link";
const ArticlePage = ({ searchParams }: Props) => {
if (
(searchParams && Object.entries(searchParams).length === 0) ||
!searchParams
) {
return notFound();
}
const article: NewsData = searchParams;
return (
<article className="mt-6">
<section className="flex flex-col lg:flex-row pb-24 px-0 lg:px-10">
<img
src={article.image === "null" ? "/no-image.jpeg" : article.image}
alt={article.title}
className="h-50 max-w-md mx-auto md:max-w-lg lg:max-w-xl object-contain rounded-lg shadow-md"
/>
<div className="px-8">
<Link legacyBehavior href={article.url || ""}>
<a target="_blank">
<h1 className="headerTitle hover:underline cursor-pointer px-0 pb-2">
{article.title}
</h1>
</a>
</Link>
<div className="flex divide-x-2 space-x-4">
<h2 className="font-bold">
By: {article.author !== "null" ? article.author : "Unknown"}
</h2>
<h2 className="font-bold pl-4">Source: {article.source}</h2>
<p className="pl-4">
<LiveTimestamp
time={
article.published_at === "null" ? "" : article.published_at
}
/>
</p>
</div>
<p className="pt-4 text-lg">{article.description}</p>
</div>
</section>
</article>
);
};
export default ArticlePage;
You just need to put the article page in [bracket] to make it dynamic so next js can fetch all pages otherwise it would display blank----
change article folder to [article]
more reference https://nextjs.org/docs/routing/dynamic-routes
Hi so I have these radio buttons where I want to save their data as json in my Postgres db . It is not being sent I get a message.success back that says I did but when i check my db nothing happens.
I don't exactly know where I am wrong so if u can help please do share.
PS: Im using Ant Design vue that's where the a- come from .
I do click on a button and it opens a modal where I have the radio buttons :
<template #modalite="{ record }">
<span>
<a-button
#click="showModalite(record)"
class="btn btn-sm btn-light mr-2"
>
<i class="fe fe-edit mr-2" />
Modalité
</a-button>
</span>
</template>
and here is my buttons code :
<a-modal
v-model:visible="visible"
:width="500"
#ok="ChangeModalite(modelInfo)"
>
<div class="row">
<a-radio-group name="radioGroup" v-model:value="traitement">
<div class="col-md-6">Négociation directe</div>
<div class="col-md-3">
<a-radio value="Négociation directe" v-model:checked="modalite.negociation" />
</div>
<div class="col-md-6">Appel à concurrence</div>
<div class="col-md-3">
<a-radio value="Appel à concurrence" v-model:checked="modalite.concurrence"/>
</div>
</a-radio-group>
</div>
</a-modal>
The script :
setup() {
const visible = ref(false)
const traitement = ref('Négociation directe');
const modalite = ref({
negociation:false,
concurrence:false,
})
const showModalite = (record) => {
modelInfo.value = record
modalite.value = { ...modalite.value, ...record.modalite }
visible.value = true
}
const ChangeModalite = (record) => {
console.log(record.id+": "+traitement.value)
axios.patch('/prop/' + record.id,{
modalite:modalite.value,
})
.then(()=>{
record.modalite=modalite.value
Object.assign(
dataSource.value.filter((item) => record.id === item.id),
record,
)
message.success(`successfully updated !!`)
visible.value = false
})
.catch((e)=>{
message.warning("smthg wrong ")
})
}
return {
dataSource,
modelInfo,
showModalite,
ChangeModalite,
modalite,
traitement,
}
},
}
So what happens now is i get the 'succefully updated ' msg without being really updated.where did i miss something?
I changed the type from json to string in my db everything works fine when I changed this line :axios.patch('/prop/' + record.id,{ modalite:modalite.value, }) to this axios.patch('/prop/' + record.id,{ modalite:traitement.value, })
so yeah data gets updated, still don't know why with the json type it's not working but at least i found a way if u have an explanation or suggestion it will be appriciated .
so I have a component that is rendering a form and it also is pre-filling the fields with data received from ajax request.
My issue is that I want to not only be able to edit fields but also add new fields to submit at the same time, so because of this I am trying to initialize my pre-filled data and new data into the same Object to be submitted with my ajax request. With my current set up the form-data is not consistently filling in the fields before the form is rendered.
This is the form template
<form #submit.prevent="editThisWorkflow" class="d-flex-column justify-content-center" >
<div>
<input type="text" v-model="workflowData.workflow">
</div>
<div >
<div v-for="object in workflowData.statuses" :key="object.id">
<input type="text" v-model="object.status">
</div>
<div v-for="(status, index) in workflowData.newStatuses" :key="index">
<input type="text" placeholder="Add Status" v-model="status.value">
<button type="button" #click="deleteField(index)">X</button>
</div>
<button type="button" #click="addField">
New Status Field
</button>
</div>
<div>
<div>
<button type="submit">Save</button>
<router-link :to="{ path: '/administrator/workflows'}" >Cancel</router-link>
</div>
</div>
</form>
This is the script
data() {
return {
workflowData: {
id: this.$store.state.workflow.id,
workflow: this.$store.state.workflow.workflow,
statuses: this.$store.state.workflow.statuses,
newStatuses: []
},
workflowLoaded: false
}
},
computed: {
...mapGetters(['workflow']),
},
methods: {
...mapActions(['editWorkflow']),
editThisWorkflow() {
this.editWorkflow({
id: this.workflowData.id,
workflow: this.workflowData.workflow,
statuses: this.workflowData.statuses,
newStatuses: this.workflowData.newStatuses
})
},
addField() {
this.workflowData.newStatuses.push({ value: ''});
},
deleteField(index) {
this.workflowData.newStatuses.splice(index, 1);
}
And this is the store method to submit the data
editWorkflow(context, workflowData) {
axios.patch('/workflowstatuses/' + workflowData.id, {
workflow: workflowData.workflow,
statuses: workflowData.statuses,
newStatuses: workflowData.newStatuses
})
.then(response => {
context.commit('editWorkflow', response.data)
})
.catch(error => {
console.log(error.response.data)
})
},
My problem comes in here
data() {
return {
workflowData: {
id: this.$store.state.workflow.id,
workflow: this.$store.state.workflow.workflow,
statuses: this.$store.state.workflow.statuses,
newStatuses: []
},
workflowLoaded: false
}
},
Is there a better way to set this part??
workflowData: {
id: this.$store.state.workflow.id,
workflow: this.$store.state.workflow.workflow,
statuses: this.$store.state.workflow.statuses,
newStatuses: []
},
If you only need to assign store values to your form once then you can use mounted function.
mounted: function() {
this.id = this.$store.state.workflow.id
this.workflow = this.$store.state.workflow.workflow
this.statuses = this.$store.state.workflow.statuses
},
data() {
return {
workflowData: {
id: '',
workflow: '',
statuses: '',
newStatuses: []
},
workflowLoaded: false
}
},
the data property does not accept this, I usually use arrow function in this question because it prohibits me from using this, and prohibits my team from also using this within the data.
Declare all necessary items within the datato maintain reactivity, and assign the value within the mounted of the page.
mounted() {
this.workflowData.id = this.$store.state.workflow.id
this.workflowData.workflow = this.$store.state.workflow.workflow
this.workflowData.statuses = this.$store.state.workflow.statuses
},
data: () => ({
workflowData: {
id: '',
workflow: '',
statuses: '',
newStatuses: []
},
workflowLoaded: false
}
},
})
The way how I resolved this problem turned out to be simpler than most of the solutions presented here. I found it hard to reach data from this.$store.state due to Vuejs life cycle. And assigning values to v-mode tourned out to be impossible because "v-model will ignore the initial value, checked or selected attributes found on any form elements. It will always treat the Vue instance data as the source of truth."
Solution
To pre-fill the field with data received from ajax request e.g. input field of type email I did as follow.
1st. I saved the output of my ajax request in application's storage (Cookies) -it can be Local Storage or Session, depended what is appropriate to you.
2nd. I populated my Vuex's store (single source of truth) with the data from my application storage. I do it every time when I reload a page.
3rd. Instead of binding a data to v-model in Vuejs life cycle, or using value attribute of html input (<input type="email" value="email#example.com">). I Pre-filled input by populating placeholder attribute of html with data coming from Vuex store like this:
<input v-model="form.input.email" type="email" name="email" v-bind:placeholder="store.state.user.data.email">
I have a problem with React, so I created script and it doesn't work.
This should:
Render first state step (it's working) (Component First)
Here is error, it don't see default values.(name & email
After click Save And Continue it should save files to data.
And going to next steps in cases.
The error is
bundle.js:34147 Uncaught ReferenceError: email is not defined
function send(e){
e.preventDefault()
}
function nextStep(){
this.setState({
step:this.state.step + 1
})
}
function nextStep(){
this.setState({
step:this.state.step - 1
})
}
function saveAndContinue(e) {
e.preventDefault()
// Get values via this.refs
var data = {
name : this.refs.name.getDOMNode().value,
email : this.refs.email.getDOMNode().value,
}
this.props.saveValues(data)
this.props.nextStep()
};
var fieldValues = [
name : null,
email : null,
];
function saveValues(fields) {
return (
fieldValues = Object.assign({}, fieldValues, fields)
);
}
class Registration extends React.Component{
constructor () {
super()
this.state = {
step:1
}
}
render() {
switch (this.state.step) {
case 1:
return <First fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues} />
case 2:
return <Two fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues}/>
case 3:
return <Third fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues}/>
case 4:
return <Success fieldValues={fieldValues} />
}
}
}
class First extends React.Component{
render(){
return(
<form onSubmit ={send}>
<div className="group">
<input className="text" type="text" ref="name" defaultValue={this.props.fieldValues.name}/>
<span className="highlight"></span>
<span className="bar"></span>
<label>Write Name</label>
</div>
<div className="group">
<input className="text" type="email" ref="email" defaultValue={this.props.fieldValues.email} />
<span className="highlight"></span>
<span className="bar"></span>
<label>Write Your Mail</label>
</div>
<button onClick={this.saveAndContinue}>Save and Continue</button>
</form>
)
}
}
There is no Two, Third and Success classes in your code, so I'm assuming they are similar to the First class.
A global function doesn't need this keyword. But in this case, you have to put saveAndContinue inside First class if it need to access the state.
In React, normally you don't have to set default value for input.
Link the input value to the state, and then setState in onChange event.
The string in placeholder is shown when the state is empty.
The code below shows how to work with input tag in React:
<input
value={this.state.inputValue}
onChange={e => {
this.setState({ inputValue: e.target.value });
}}
type="text"
placeholder="default value"
/>
Note that the state will updates onChange rather than click the save button.
Does this solve your problem?
I've set up a react web application that's currently listing all "Employees" from a mongodb.
I'm now trying to "add" employees to the database through a react frontend form.
I've managed to pass the data from the form to the application but I'm unsure of the process I need to go through to actually get that data solidified into an object and stored in the api.
Please excuse my code, it's disgusting as this is my first week learning react(honestly with little js knowledge, that's another story) and I've just patched together like 20 tutorials....
Here's my Form class:
class Form extends React.Component {
state = {
fullname: '',
}
change = e => {
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state)
this.setState({
fullname: ''
})
}
render() {
return <div>
<form>
<input name="fullname" placeholder="Full Name" value={this.state.fullname} onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
</div>
}
}
and my Listing(?) class:
class EmployeeList extends React.Component {
constructor(props) {
super(props);
this.state = {employee: []};
this.EmployeeList = this.EmployeeList.bind(this)
this.componentDidMount = this.componentDidMount.bind(this)
}
componentDidMount() {
this.EmployeeList();
}
EmployeeList() {
fetch('/api/employees').then(function(data){
return data.json();
}).then( json => {
this.setState({
employee: json
});
console.log(json);
});
}
onSubmit = fields => {
console.log('app component got: ', fields)
}
render() {
//return a mapped array of employees
const employees = this.state.employee.map((item, i) => {
return <div className="row">
<span className="col-sm-6">{item.fullname}</span>
<span className="col-sm-2" id={item.action1}></span>
<span className="col-sm-2" id={item.action2}></span>
<span className="col-sm-2" id={item.action3}></span>
</div>
});
return <div>
<Form onSubmit={fields => this.onSubmit(fields)}/>
<div className="container">
<div className="row">
<div className="col-sm-6 bg-warning"><h3>Full Name</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 1</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 2</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 3</h3></div>
</div>
</div>
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">{ employees }</div>
</div>
</div>
}
}
I've managed to pass the data to the listing app evident by
onSubmit = fields => {
console.log('app component got: ', fields)
}
But how can I go about making a post request to store this data I send into an object on the db? And then also reload the page so that the new list of all employee's is shown?
Thanks so much for your time!
You can use fetch API to make POST request as well. Second parameter is the config object wherein you can pass the required request configurations.
fetch('url', {
method: 'post',
body: JSON.stringify({
name: fields.fullname
})
})
.then(response) {
response.json();
}
.then( json => {
this.setState({
employee: json
});
});
Additional Request Configs which can be used :
method - GET, POST, PUT, DELETE, HEAD
url - URL of the request
headers - associated Headers object
referrer - referrer of the request
mode - cors, no-cors, same-origin
credentials - should cookies go with the request? omit, same-origin
redirect - follow, error, manual
integrity - subresource integrity value
cache - cache mode (default, reload, no-cache)