Set input class if two variables are equal with VueJS - css

I'd like my <input> class to be set to saved when its value $event.target.value is equal to my Vuex state variable this.$store.state.company.name.
How should I set it ?

Check out Class and Style Bindings in the docs, you could bind saved class to a computed property which checks if the input equals Vuex state variable, here is an example:
new Vue({
el:"#app",
data: {
inputData : '',
},
computed: {
validated(){
// Here we should compare data from vuex and the user input
return this.inputData == "mohd"
}
}
})
.saved {
border: 1px green solid;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.min.js"></script>
<div id="app">
<label> write mohd to validate it</label>
<input type="text" :class="{saved: validated}" v-model="inputData">
</div>

InputSave.vue
<template>
<input type="text" v-model="myInput" :disabled="isEqual" :class="isEqual ? 'saved" : ''" />
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data() {
myInput: ''
},
methods: {
...mapGetters
},
computed: {
isEqual: function() {
return this.myInput === this.myGgetter
}
}
}
</script>

Solved here : https://jsfiddle.net/9a9cvu7b/10/ , in fact you can solve it with plain JavaScript.
Just instanciate the check method on all your dynamics generated inputs attached to event #input (who is thrown each time the input value change on keyup)
<div id="app">
<input
type="text"
#input="check">
</div>
const app = new Vue({
el:'#app',
data() {
return {
wordToCheck: 'toto'
}
},
methods: {
check(event) {
const input = event.target;
// replace this.wordToCheck by this.$store.state.company.name
if(input.value === this.wordToCheck) {
input.classList.add("saved")
} else {
input.classList.remove("saved")
}
}
}
})

Related

How to populate FormKit input fields with dynamic data fetched from a database

I'm making a fullstack app with vue3, axios using FormKit. For editing existing records I want to populate the input fields with the current data fetched from a mysql database. I stripped down the code to everything needed to display my problem, which in this code example is populating the FormKit input field with the lotnumber I fetched via the asynchronous function "getLotById". The lotnumber appears in the paragraph section but not in the input field. How can I properly delay the rendering of the FormKit element until the lotnumber has been fetched? Here's my code:
<script>
// import axios
import axios from "axios";
export default {
name: "LotEdit",
data() {
return {
lotnumber: this.lotnumber
}
},
props: {
lotid: Number
},
created: async function () {
await this.getLotById();
},
methods: {
// Get Lot By Id
async getLotById() {
try {
const response = await axios.get(`http://localhost:5000/lot/${this.$route.params.id}`);
this.lotnumber = response.data.lotnumber;
console.log(response.data);
}
catch (err) {
console.log(err);
}
},
}
};
</script>
<template>
<div>
<FormKit
type="text"
name="lotnumber"
label="lotnumber"
placeholder=""
validation="required"
:value="lotnumber"
/>
</div>
<div>
<p> Here the lotnumber appears: {{ lotnumber }}</p>
</div>
</template>
I suggest using a v-model on the FormKit input. Because it is two-way bound it means as soon as the async/await completes the data is populated on the template too. Something like...
<FormKit
v-model="lotnumber"
type="text"
name="lotnumber"
label="lotnumber"
placeholder=""
validation="required"
:value="lotnumber"
/>
Getting a little smarter I managed to solve the problem in the following way:
<script>
// import axios
import axios from "axios";
export default {
name: "LotEdit",
data() {
return {
lotnumber: this.lotnumber
}
},
props: {
lotid: Number
},
mounted: async function () {
const response = await this.getLotById();
const node = this.$formkit.get('lotnumber')
node.input(response.data.lotnumber, false)
},
methods: {
// Get Lot By Id
async getLotById() {
try {
const response = await axios.get(`http://localhost:5000/lot/${this.$route.params.id}`);
console.log(response.data);
return response;
}
catch (err) {
console.log(err);
}
},
}
};
</script>
<template>
<div>
<FormKit
type="text"
id="lotnumber"
name="lotnumber"
label="lotnumber"
placeholder=""
validation="required"
:value="lotnumber"
/>{{ lotnumber }}
</div>
</template>
Feel free to post any recommendations as I'm not a pro yet...
I'm also still figuring out how to handle controlled forms but I guess an alternative way to do it is with Form Generation
<script>
export default {
// ...
async setup() {
try {
const response = await axios.get(`http://localhost:5000/lot/${this.$route.params.id}`);
const schema = [
{
$formkit: "text",
label: "Lot Number",
value: response.data.lotnumber,
validation: "required",
},
];
} catch (err) {
console.log(err);
}
return { schema }
}
// ...
}
</script>
<template>
<FormKit type="form">
<FormKitSchema :schema="schema" />
</FormKit>
</template>

How to make an object passed as a prop in Vue 3 reactive?

I need to reactively change my component when a field in passed object changes.
<template>
<my-component :prop="prop" />
</template>
<script>
export default {
data() {
return {
prop: {
key: 'value',
flag: true
}
}
}
}
</script>
mycomponent.vue
<template>
<div v-if="flag">Yay, it's a flag!</div>
<div v-else>I am very sad rn</div>
</template>
<script>
export default {
props: {
prop: Object
},
setup(props) {
const prop = ref(props, 'prop')
const flag = // *
return { flag }
}
}
</script>
Don't know what to do here, prop.flag, prop.value.flag doesn't work.
I also tried something like const flag = ref(prop, 'flag') and then flag.value, or even const flag = req(prop.value, 'flag'), but no luck.
Props are accessible and reactive in components should you declare them. Since you haven't, they won't be available.
For example, this is all you need:
<template>
<div v-if="prop.flag">Yay, it's a flag!</div>
<div v-else>I am very sad rn</div>
</template>
<script>
export default {
props: {
prop: Object
}
}
</script>
Just use toRef or toRefs
<template>
<div v-if="flag">Yay, it's a flag!</div>
<div v-else>I am very sad rn</div>
</template>
<script>
import { toRefs, toRef } from 'vue';
export default {
props: {
prop: Object
},
setup(props) {
const { prop } = toRefs(props);
//alternative
const prop = toRef(props, 'prop');
const flag = // *
return { flag }
}
}
</script>

CASL-Vue Can component hides components for both authorized and unauthorized user

I have a component that I'll like to show if user has permission but the Can component seem to hide the component regardless of user permission.
Following is my ability.js module
import {Ability, AbilityBuilder} from "#casl/ability";
export default function defineAbilityFor(hasRole) {
const { can, cannot, build } = new AbilityBuilder(Ability)
if (hasRole !== undefined) {
console.log(hasRole)
can('manage', 'all')
can('create', 'all')
} else {
can('read', 'all')
cannot('create', 'all')
}
return build()
}
I added castle as a my main.js as follows:
const app = createApp(App)
app.use(router)
app.use(store)
app.use(abilitiesPlugin, ability(), {
useGlobalProperties: true
})
app.component(Can.name, Can)
app.mount('#app')
And the following is the component in which i need to do permission check.
<template>
<div class="d-flex flex-column px-4">
<div class="d-flex justify-content-end">
<Can I="create" an="Institution">
<button class="btn btn-sm btn-outline-success" #click="addInst = true">{{ action }}</button>
</Can>
</div>
<ListInstitutions v-if="addInst === false"/>
<Can I="create" an="Institution">
<AddInstitution v-if="addInst === true" #created="closeModal"/>
</Can>
</div>
</template>
<script>
import AddInstitution from "#/components/institution/AddInstitution";
import ListInstitutions from "#/components/institution/ListInstitutions";
import { Can } from "#casl/vue";
export default {
name: 'InstitutionPage',
components: {AddInstitution, ListInstitutions, Can},
data() {
return {
addInst: false,
action: this.addInst ? 'Institutions' : 'Add Institution'
}
},
methods: {
closeModal() {
this.addInst = false
}
}
}
</script>
Thank you so much #Sergii Stotskyi.
I eventually defined ability in a definedAbility.js file as follows:
import { AbilityBuilder, Ability } from "#casl/ability";
export default function defineAbilityFor(userRole) {
const { can, cannot, build } = new AbilityBuilder(Ability);
if (userRole === 'user') {
can ('read', 'Institution')
cannot(['create', 'update'], 'all')
}
if (userRole === 'reg_admin_stroke' || userRole === 'admin') {
can('manage', 'all')
}
return build()
}
And then in every component where ability is required, inside the setup() method I create the ability as follows:
...
import definedAbility from '#/services/definedAbility.js'
...
const ability = definedAbility(userRole.value)
return {
ability
}
And In my template did something like:
<div v-if="ability.can('create', 'Institution')">
<button class="btn btn-sm btn-outline-success" #click="addInst = true">{{ action }}</button>
</div>

Vue.js how can i loop throw an array to add components to the dom according to array items

I am making an app that communicate with an api and fetch data,home page changes every day so i can't just add static components to it,
i need to create it according to the data that comes from the api.
i have a component for the home page called Home.vue
this component can have one or more Carousels depending on the data that i'am fetching.
i also have Carousel.vue which is responsible about displaying images and it had it's own props.
the question is :
How to add component to the dom from loop
this is Home.vue where i am making the loop :
<template>
<div>
<!--I Need The Loop right here-->
</div>
</template>
<script>
export default {
components: {},
data() {
return {
page_content: [],
widgets: [],
}
},
created() {
this.getHomeContent();
},
methods:
{
getHomeContent() {
window.axios.get(window.main_urls["home-content"]).then(response => {
this.page_content = JSON.parse(JSON.stringify(response.data));
console.log(this.page_content);
for (let index in this.page_content) {
switch (this.page_content[index].type) {
// if type is banner
case 'banner':
switch (this.page_content[index].display) {
// if display is carousel
case 'carousel':
console.log('carousel')
// end if display is carousel
this.widgets.push({
'type': 'Carousel',
'images': this.page_content[index].items,
})
}
// end if type is banner
}
}
});
}
}
}
</script>
and this is Carousel.vue which i need to be imported when needed with passing props :
<template>
<div>
<div >
<VueSlickCarousel>
<div v-for="image in images">
<img src="{{img}}">
</div>
</VueSlickCarousel>
</div>
</div>
</template>
<script>
import VueSlickCarousel from 'vue-slick-carousel'
import 'vue-slick-carousel/dist/vue-slick-carousel.css'
import 'vue-slick-carousel/dist/vue-slick-carousel-theme.css'
export default
{
components: {VueSlickCarousel},
name:'Carousel',
props:[
'images'
],
methods:
{
}
}
</script>
how to add Carousel.vue component to Home.vue dynamically some thing like:
if(data.display == 'carousel')
{
<carousel images="data.images"></carousel>
}
Import the component to your Home.vue :
import Carousel from './Carousel.vue'
export default {
components: {Carousel},
}
Then loop in your template:
<carousel v-for="(widget,index) in widgets" :key="index" :images="widget.images"/>
Best to use a widget.id rather than index for the key prop
This is the correct answer !
<template>
<div>
<template v-for="widget in widgets">
<div v-if="widget.type == 'carousel'" :key="widget.type">
<carousel
:images="widget.images"
:arrows ="widget.arrows"
:dots = "widget.dots"
>
</carousel>
</div>
</template>
</div>
</template>
<script>
import Carousel from './widgets/Carousel.vue'
export default {
components: {Carousel},
data() {
return {
page_content: [],
widgets: [],
}
},
created() {
this.getHomeContent();
},
methods:
{
getHomeContent() {
window.axios.get(window.main_urls["home-content"]).then(response => {
this.page_content = JSON.parse(JSON.stringify(response.data));
console.log(this.page_content);
for (let index in this.page_content) {
switch (this.page_content[index].type) {
// if type is banner
case 'banner':
switch (this.page_content[index].display) {
// if display is carousel
case 'carousel':
console.log('carousel')
// end if display is carousel
this.widgets.push({
'type': 'carousel',
'arrows':true,
'dots':true,
'images': this.page_content[index].items,
})
}
// end if type is banner
}
}
});
}
}
}
</script>

Intercept an async action in redux

I'm building a simple notes app using react with redux. My create notes action looks like this:
import axios from "axios";
const URL = 'http://localhost:3002/api/';
export function createNote(data = {}) {
return {
type: 'CREATE_NOTE',
payload: axios.post(URL + 'notes/', data),
};
}
And I've got the following in my reducer:
// Create
case 'CREATE_NOTE_PENDING':
{
return {
...state,
creating: true
};
}
case 'CREATE_NOTE_FULFILLED':
{
return {
...state,
creating: false,
notes: action.payload.data
};
}
case 'CREATE_NOTE_REJECTED': {
return {
...state,
creating: false,
error: action.payload
};
}
And this is my Notes class:
function mapStateToProps(store) {
return {
data: store.note.notes,
fetching: store.note.fetching,
creating: store.note.creating,
error: store.note.error,
};
}
class Notes extends Component {
componentWillMount() {
this.props.dispatch(fetchNotes());
}
create() {
this.props.dispatch(createNote({
title: this.refs.title.value,
content: this.refs.content.value,
}));
this.refs.title.value = '';
this.refs.content.value = '';
}
render() {
return (
<div className="App">
<h1 className="text-center">Notepad</h1>
<div className="row">
<div className="col-md-6 col-md-offset-3">
<div className="form-group">
<input ref="title" placeholder="Create a note" className="form-control" disabled={this.props.creating} />
</div>
<div className="form-group">
<textarea ref="content" className="form-control" disabled={this.props.creating} />
</div>
<button onClick={this.create.bind(this)}
type="submit"
className="btn btn-primary"
style={{float: 'right'}}
disabled={this.props.creating} >Save</button>
</div>
</div>
No on create I'm going to disable the form till I get an answer from the server and reset the content inside the form. My question is how to reset the content of the form when I've got response, i.e. when the form is enabled?
If you want to control the content of input and textarea, you should use controlled components. That is to provide a value attribute to, e.g. input
<input value={this.state.title} placeholder="Create a note" className="form-control" disabled={this.props.creating} />
You can use either internal state or redux state for the value. Then you are able to control the value by setState or redux actions.

Resources