Concatenating multiple classes in Vue.js - class

I want to write in less code a function that will add the active classname and automatically removes all the other active class names. But there is also a unique class name needed for JavaScript in my case. But want to put that all in class name. How can I make this a valid classname. Is there a way to do that so it will not conflict with each other.
<ul class="three">
<li
v-for="(post, index) in listData.data"
:key="index"
:class="'list-item unordered-list ' + post.name.toLowerCase() + { active : activeName == post.name}"
#click="showInfo(post.name, post.description)">
{{ post.name }}
</li>
</ul>

Have a look at the object syntax or array syntax of class binding, which allows binding to an object or array returned by a value. That way you can simplify complex class or style combinations by calling a function from the template, like the example from the docs:
<div v-bind:class="classObject"></div>
...
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}

<ul class="three">
<li
v-for="(post, index) in listData.data"
:key="index"
:class="['list-item', 'unordered-list ', post.name.toLowerCase(), { active: activeName == post.name }]"
#click="showInfo(post.name, post.description)">
{{ post.name }}
</li>
</ul>

Related

How to use v-bind to add "dynamic" class to button

so i have a getter (neither getter or paged component have all desired status values) im thinking of somehow using the getter for this without success
getStatusValues: (state) => {
return [
{ id: 0, name: i18n.t('OK') },
{ id: 1, name: i18n.t('Running') },
{ id: 2, name: i18n.t('Error') }
]
},
and i want to bind a class to a button in this self paged component
<div class="objs">
<div
v-for="obj in objPage"
:key="obj.id"
class="obj"
>
<button
:class="{class1:obj.status === 'OK', class2: obj.status === 'NotRunning', class3: obj.status === 'Running', Err: obj.status === 'Error'}"
#click="Dialog(obj)"
>
{{ obj.id }}
</button>
</div>
<div
v-for="i in (objPage.length < 9) ? 9 - objPage.length : 0"
:key="i"
class="empty"
/>
</div>
is there a way i could perhaps make all the status values dynamically be the class names and only have to do one check for {classname:obj.status === "classname"} cause the way i have went about it is not the best and i want to find a diffrent one
The answer is
:class="obj.status"

How to create Date(now) within Vue.js to use in a computed property?

I have an array of calendar events with a pre-formatted date: 20190517T010000Z.
I need to filter the array based upon the following:
1) Future Events (any events occurring greater than NOW)
2) Past Events (any events that occurred less than NOW)
3) New Events created within the past 7 days (NOW - 7 days)
I have an example hardcoded below - but need NOW to be dynamic and based upon the user's system time. I can't figure out how to get NOW in the same format as the datetime format I'm getting in the my array. Also, I don't know where in my code it should reside (within a separate JS file or within the Vue component)?
Once I have it working within the component listed below, it will be moved to a VUEX Getter.
<template>
<div class="text-left m-4 p-4">
<div>
Total Number Events: {{ allEvents.length }}
</div>
<div>
Events Created In Last 7 Days: {{ createdEvents }}
</div>
<div>
Future Events: {{ futureEvents }}
</div>
<div>
Past Events: {{ pastEvents }}
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['allEvents']),
futureEvents () {
return this.$store.state.allEvents.filter(allEvents => allEvents.dtstart >= '20190517T010000Z').length
},
pastEvents () {
return this.$store.state.allEvents.filter(allEvents => allEvents.dtstart <= '20190517T235900Z').length
},
createdEvents () {
return this.$store.state.allEvents.filter(
allEvents => allEvents.created >= '20190511T235900Z' && allEvents.created <= '20190517T235900Z' )
.length
}
},
}
</script>
The above code works - but it is hardcoded right now and it needs to be dynamic. Any thoughts or suggestions would be welcome.
All your date time objects should be persisted as UNIX timestamps (UTC seconds since 1/1/1970). This will allow you to compare times across systems no matter the timezone.
Provided that the events in your Vuex store are stored with dstart being a UNIX timestamp, the following snippet should give you the dynamic behaviour you want.
I've created a now attribute in the component's data. This gets updated every second, causing the computed properties to refresh.
Additionally, if the allEvents property in the Vuex store updates, the computed properties will also refresh.
<template>
<div class="text-left m-4 p-4">
<div>
Total Number Events: {{ allEvents.length }}
</div>
<div>
Events Created In Last 7 Days: {{ createdEvents }}
</div>
<div>
Future Events: {{ futureEvents }}
</div>
<div>
Past Events: {{ pastEvents }}
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data () {
return {
now: new Date().getUTCSeconds()
}
},
created () {
this.scheduleUpdateNow();
},
methods: {
updateNow() {
this.now = new Date().getUTCSeconds();
this.scheduleUpdateNow();
},
scheduleUpdateNow() {
setTimeout(this.updateNow, 1000);
}
},
computed: {
...mapState(['allEvents']),
futureEvents () {
return this.$store.state.allEvents.filter(allEvents => allEvents.dtstart > this.now).length
},
pastEvents () {
return this.$store.state.allEvents.filter(allEvents => allEvents.dtstart <= this.now).length
},
createdEvents () {
return this.$store.state.allEvents.filter(
allEvents => allEvents.created >= this.now && allEvents.created <= this.now).length
}
}
}
</script>

REACT Multiple Registration

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?

How to search collection in meteor with more parameters

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.

Symfony custom fields on add

On my Symfony2 project I got a list of arguments :
<ul class="arguments" data-prototype="{{ form_widget(form.arguments.vars.prototype)|e('html_attr') }}">
{% for argument in form.arguments %}
{#<li>{{ form_row(argument) }}</li>#}
<li>
{{ form_row(argument.name, {'label': 'Name'}) }}
{{ form_row(argument.french_description) }}
{{ form_row(argument.english_description) }}
{{ form_row(argument.return) }}
{{ form_row(argument.type) }}
</li>
{% endfor %}
</ul>
But I got a button "Add arguments", creating a new < li >< /li > with arguments fields. The problem is the new argument don't got the custom label "Name".
EDIT :
I add the arguments with a buton calling a JS function to add my fields on the page. Then when I click on submit it save it to database.
$('#add_argument_link').on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// setup an "add a argument" link
var $newLinkLi = $('<li></li>');
// add a new argument form (see next code block)
addArgumentForm($collectionHolder, $newLinkLi);
addTagFormDeleteLink($newLinkLi);
});
Thanks for your help.
You can try something like that :
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var $container = $('div#name_of_your_div');
var index = $container.find(':input').length;
$('#add_argument').click(function(e) {
addArgument($container);
e.preventDefault();
return false;
});
if (index == 0) {
addArgument($container);
} else {
$container.children('div').each(function() {
addDeleteLink($(this));
});
}
function addArgument($container) {
var template = $container.attr('data-prototype')
.replace(/__name__label__/g, 'Argument number' + (index+1))
.replace(/__name__/g, index);
var $prototype = $(template);
addDeleteLink($prototype);
$container.append($prototype);
index++;
}
function addDeleteLink($prototype) {
var $deleteLink = $('Delete');
$prototype.append($deleteLink);
$deleteLink.click(function(e) {
$prototype.remove();
e.preventDefault();
return false;
});
}
});
</script>