Form.Field error and Input error properties, problem with property type - semantic-ui

I noticed a problem today with Semantic UI React and using an Input component inside a Form.Field component and the usage of the error property:
const ScheduleTypeFields = ({ form }: FieldProps<JobEntryType>) => (
<Form.Field>
<Form.Input label='Schedule' required error={form.touched.schedule && form.errors.schedule}>
<Input label={<Dropdown basic options={ScheduleTypes} name='cronType' id='cronType' onChange={(_, opts) => form.setFieldValue('cronType', opts.value)} onBlur={form.handleBlur} value={form.values.cronType} />}
placeholder='Specify in CRON format, i.e. * * * * *'
labelPosition='left' name='schedule' id='schedule' onChange={form.handleChange} onBlur={form.handleBlur} value={form.values.schedule} />
</Form.Input>
</Form.Field>
)
The problem I am facing is simple:
Warning: Failed prop type: Invalid prop `error` supplied to `FormField`.
in FormField (created by FormInput)
in FormInput (created by ScheduleTypeFields)
in div (created by FormField)
in FormField (created by ScheduleTypeFields)
in div (created by FormGroup)
in FormGroup (created by ScheduleTypeFields)
in ScheduleTypeFields (created by FieldInner)
in FieldInner (created by Context.Consumer)
in FormikConnect(FieldInner) (created by InnerJobForm)
in form (created by Form)
in Form (created by InnerJobForm)
in InnerJobForm (created by Formik)
in Formik (created by JobForm)
in JobForm (created by CreateJob)
in CreateJob (created by Context.Consumer)
in Route (created by App)
in Switch (created by App)
in div (created by Container)
in Container (created by App)
in Router (created by App)
in App
I assume the problem is with the signature for error in Input vs the signature of error in Form.Field, in the former is boolean while any? in the later and Form.Field is passing error to Input, hence the error.
I have to do it in this way because I have a custom label with a control in my input control, I tried using nested component but I couldn't, maybe I am doing something wrong?
Cheers

I don't think you can use Input component inside Form.Input since Form.Input renders as input in html. So, this means that you have at the end rendered something like this:
<input>
<input></input>
</input>
which is semantically wrong and is reporting error in pure HTML as well.
You should restructure your code a bit differently. I suggest putting Input component on the same level as Form.Input

Related

Vue3 Prop passing to data item to be able to manipulate values, not modelling data item actually, why?

I am using vue3 and wonder how to pass data the correct way.
My Component structure is one table (items loaded via pinia store): XTableComponent
The XTableComponent has a child: XModalComponent. In the rendered table I have a button in each row. #click on that stores the current item in a data item
XTableComponent:
<template>
...that mentioned table in each line a button with #click and the item in the iteration as param
<x-model-component v-if="currentItem" :item="currentItem ref="x-modal"></x-modal-component>
</template>
<script>
export default {
data: () => {
return {
currentItem: {},
itemListStore: useItemListStore()
}
},
computed: {
itemList() {
return this.itemListStore.list
}
methods: {
showModal(item){
this.currentItem = item
this.$refs['x-modal'].show()
}
}
}
</script>
My Child component looks a bit like this:
XModalComponent:
<template>
....
<input v-model:value="innerItem.something" type="text">
<button #click="save">save</button>
</template>
<script>
export default {
props: {
item: Object
},
data: () => {
return {
innerItem: {}
}
}
mounted() {
this.innerItem = item
},
methods: {
save() {
console.log(this.innerItem) //this does not show the manipulated value of `something`
}
}
}
</script>
Now, if I manipulate the input in my child component, and trigger a click event, the value does not get changed on my data item ...
what did I get wrong in vue3 with reactiveness, proxeis and passing props?
p.s. my code is kind of pseude code here, so please be fair with me on typos, or obvious parts
that are missing
p.p.s. I am used to vue2 quite well, so maybe I mix concepts. please tell me that too.
p.p.p.s. my table renders correctly, the modal looks fine. i double checked all names and typos.
So, as we figured out, the problem came from the way innerItem.something was bound to the input, and some confusion around the v-model directive.
As a recap, the v-model directive is short-hand for setting a prop on a component and listening to an event which updates the value.
In Vue 2, that was:
<child-component
:value="myValue"
#input="(nevValue) => myValue = newValue"
/>
which is equivalent to
<child-component v-model="myValue"/>
and it allows a variable to be changed by parent as well as child ("two-way binding"). Note that property name and event matches that of a HTML input element (the "value" attribute and the "input" event), probably because it represents the most familiar case, where a value is bound to an input:
<input type="text" v-model="myText"/>
However, to allow for multiple two-way bindings on a component, Vue 2 also introduced a second way, which allows to bind to any of the child components props, not just "value". This is the .sync modifier:
<child-component :childComponentProp.sync="myVar"/>
which is equivalent to:
<child-component
:childComponentProp="myVar"
#update:childComponentProp="(newValue) => myVar = newValue"
/>
In Vue 3, they decided to unify the two, dropping .sync and instead allowing to pass a prop name to v-model similar to how slot names are passed to the v-slot directive, i.e. v-model:childComponentProp="myVar", and similar as v-slot alone is equivalent to v-slot:default, v-model alone is equivalent to v-model:modelValue. So it is equivalent to:
<my-component
:modelValue="myValue"
#update:modelValue="(nevValue) => myValue = newValue"
/>
But the above only applies for Vue components. When using v-model on an HTML input element, it sill behaves like in Vue 2 and binds to the "value" attribute and the "input" event. It is still equivalent to:
<input :value="myValue" #input="(nevValue) => myValue = nevValue"/>
However, that behavior is a special case of plain v-model (i.e. without a prop name). And I think this is where the confusion comes from.
Using v-model:value explicitly binds to the #update:value event, i.e. this
<input v-model:value="innerItem.something" type="text">
is equivalent to:
<input type="text"
:value="innerItem.something"
#update:value="(newValue) => innerItem.something = newValue"
/>
but that event is not sent by a plain HTML element.
So long long story short, you have to use v-model= instead of v-model:value= when binding to a native input element.
Does that make sense? Hope it helps.

Vue3 prop updates title attribute but not computed prop

I'm using an external library rendered using Vue3. It has the following component from a third part library [Edit: I realize the GitHub repo for that library is out of date, so updating with the actual code in my node_modules.]
<template>
<div class="socket" :class="className" :title="socket.name"></div>
</template>
<script>
import { defineComponent, computed } from "vue";
import { kebab } from "./utils";
export default defineComponent({
props: ["type", "socket"],
setup(props) {
const className = computed(() => {
return kebab([props.type, props.socket.name]);
});
return {
className
};
}
});
</script>
It renders based on a Socket object passed as a prop. When I updated the name property of the Socket, I see the title updated accordingly. However, the CSS/class does not update. I've tried $forceRefresh() on its parent, but this changes nothing.
Update: I was able to move the rendering code to my own repo, so I can now edit this component if needed.
Based on this updated code, it seems the issue is that the class is computed. Is there any way to force this to refresh?
The only time it does is when I reload the code (without refreshing the page) during vue-cli-service serve.
For reference, the | kebab filter is defined here:
Vue.filter('kebab', (str) => {
const replace = s => s.toLowerCase().replace(/ /g, '-');
return Array.isArray(str) ? str.map(replace) : replace(str);
});
Do filtered attributes update differently? I wouldn't think so.
I was also wondering if it could be a reactivity issue, and whether I needed to set the value using Vue.set, but as I understand it that's not necessary in Vue3, and it's also not consistent with the title properly updating.
Computed properties are reactive, however Vue does not expect you to mutate a prop object.
From the documentation:
Warning
Note that objects and arrays in JavaScript are passed by reference, so
if the prop is an array or object, mutating the object or array itself
inside the child component will affect the parent state and Vue is
unable to warn you against this. As a general rule, you should avoid
mutating any prop, including objects and arrays as doing so ignores
one-way data binding and may cause undesired results.
https://v3.vuejs.org/guide/component-props.html#one-way-data-flow
I know that this says, that you should not mutate it in the child, but the general rule is, that you should not mutate properties at all, but instead create new object with the modified data.
In your case the computed function will look for changes in the properties itself, but not the members of the properties, that is why it is not updating.

Initializing react component from asp.net

Hopefully this is a slam-dunk for someone out there...my essential problem is this: I've built up a very nice set of react components which i can render in my asp.net 4.5 mvc 6 application using react.js, flux, gulp, and browserify.
as long as i have it structured so that the react components have all the data they need everything is perfect. My issue now is that I would like to have an MVC view include the react stuff, and inject run-time properties into the top-level component as it is created. Since I'm brpowserify-ing all of my react code into a bundle, i just include the one script tag in my view:
<script src="/js/modules/AuthContainer.jsx"></script>
But whereas I would normally use JSX syntax to instantiate my component with props like this:
...the view in ASP.NET never gets translated to pure JS, so that fails.
I've also tried:
ReactDOM.render
(
React.createElement(AuthContainer, { successPath: '/home' }),
document.getElementById('reactRoot')
);
...from inside a script block in my view but i get:
Uncaught ReferenceError: AuthContainer is not defined
But i'm sure i'm exposing 'AuthContainer' via the browserify-ed bundle, so i don't understand why it's unable to resolve that component.
I know there's a React.NET way to do this, but i can't get that server-side rendering to work with my components because I'm using jQuery to fetch data in componentDidMount and the server-side rendering is choking looking for $() jQuery stuff.
I'd love to get the server side rendering going but right now i just need it to do work, one way of the other. Can someone provide a simple code snippet or gist of how to instantiate a React component from inside a cshtml file with run-time props?
One easy solution is this, just put your server side properties with Javascript in a global:
index.cshtml
<script>
var __config__ = {
base: "#MyBackEdnVariable",
initialCount: "#Count",
user: {
id: #user.id,
name: #user.name,
}
};
</script>
<script src="/js/modules/AuthContainer.jsx"></script>
And with React use that global variable:
AuthContainer.js
class AuthContainer extends React.Component {
render() {
return (
<div>{this.props.user.name}</div>
);
}
}
AuthContainer.defaultProps = {
initialCount: __config__.initialCount,
user: __config__.user
};
For posterity:
ReactDOM.render
(
React.createElement
(
MyComponent,
{
prop1: #numericValue,
prop2: '#textValue',
}
),
document.getElementById('reactRoot')
);
the magic was the jsx-alternative syntax, which i was aware of couldn't get a handle on that day. This allows you to instantiate react using pure JS and therefor just embed inside a simple script tag in your cshtml.
hth.

How to Two-way Data Binding Between Parents and grandchildren in Vue.js

I faced a problem, I solve it by cookies but I want to solve the problem without cookies. I have a component which called app-header and It has another component which called outmodal.
Now, My first Vue instance require component app-header.
var vue = new Vue({
el : "html",
data : {
title : "Site Title",
description : "description of page",
keywords : "my keywords",
view : "home",
login : "login"
},
components:{
"app-header" :require("../../components/header"),
"app-footer" :require("../../components/footer"),
"home" :require("../../views/home")
},
});
code of app-header
var Vue = require("vue");
Vue.partial("login",require("../../partials/login.html"));
Vue.partial("logged",require("../../partials/logged.html"));
module.exports = {
template : require("./template.html"),
replace : true,
components : {
outmodal : require("../outmodal")
},
props : ['login']
}
code of outmodal
var Vue = require("vue");
Vue.partial("loginModal",require("../../partials/loginModal.html"));
module.exports = {
template : require("./template.html"),
replace : true,
props : ['name'],
data : function () {
return {
userLogin : { mail : "", password : "", remember : ""}
}
},
methods : {
formSubmit : function(e){
e.preventDefault();
this.$http.post("http://example.com/auth/login",{ "email": this.userLogin.mail , "password": this.userLogin.password },function(data,status,request){
$.cookie("site_token",data.token,{expires : 1})
}).error(function(data,status,request){
});
}
}, ready : function(){
console.log("it works")
}
}
In outmodal component I connect the API and I check the login, If login will be succesfull, I want to change value of login variable in my Vue instance. I use web pack to build all requires. So I don't know how can I data binding between these files.
How can I solve It? I
The Best Solution which I found
For 0.12
http://012.vuejs.org/guide/components.html#Inheriting_Parent_Scope
for 1.0
http://v1.vuejs.org/guide/components.html#Parent-Child-Communication
for 2.0
https://v2.vuejs.org/v2/guide/components.html#Composing-Components (use props to one-way bind data from parent to child)
There are several ways of doing it, and some are mentioned in other answers:
Use props on components
Use v-model attribute
Use the sync modifier (for Vue 2.0)
Use v-model arguments (for Vue 3.0)
Use Pinia
Here are some details to the methods that are available:
1.) Use props on components
Props should ideally only be used to pass data down into a component and events should pass data back up. This is the way the system was intended. (Use either v-model or sync modifier as "shorthands")
Props and events are easy to use and are the ideal way to solve most common problems.
Using props for two-way binding is not usually advised but possible, by passing an object or array you can change a property of that object and it will be observed in both child and parent without Vue printing a warning in the console.
Because of how Vue observes changes all properties need to be available on an object or they will not be reactive.
If any properties are added after Vue has finished making them observable 'set' will have to be used.
//Normal usage
Vue.set(aVariable, 'aNewProp', 42);
//This is how to use it in Nuxt
this.$set(this.historyEntry, 'date', new Date());
The object will be reactive for both component and the parent:
I you pass an object/array as a prop, it's two-way syncing automatically - change data in the
child, it is changed in the parent.
If you pass simple values (strings, numbers)
via props, you have to explicitly use the .sync modifier
As quoted from --> https://stackoverflow.com/a/35723888/1087372
2.) Use v-model attribute
The v-model attribute is syntactic sugar that enables easy two-way binding between parent and child. It does the same thing as the sync modifier does only it uses a specific prop and a specific event for the binding
This:
<input v-model="searchText">
is the same as this:
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
Where the prop must be value and the event must be input
3.) Use the sync modifier (for Vue 2.0)
The sync modifier is also syntactic sugar and does the same as v-model, just that the prop and event names are set by whatever is being used.
In the parent it can be used as follows:
<text-document v-bind:title.sync="doc.title"></text-document>
From the child an event can be emitted to notify the parent of any changes:
this.$emit('update:title', newTitle)
4.) Use v-model arguments (for Vue 3.0)
In Vue 3.x the sync modifier was removed.
Instead you can use v-model arguments which solve the same problem
<ChildComponent v-model:title="pageTitle" />
<!-- would be shorthand for: -->
<ChildComponent :title="pageTitle" #update:title="pageTitle = $event" />
5.) Use Pinia (or Vuex)
As of now Pinia is the official recommended state manager/data store
Pinia is a store library for Vue, it allows you to share a state across components/pages.
By using the Pinia store it is easier to see the flow of data mutations and they are explicitly defined. By using the vue developer tools it is easy to debug and rollback changes that were made.
This approach needs a bit more boilerplate, but if used throughout a project it becomes a much cleaner way to define how changes are made and from where.
Take a look at their getting started section
**In case of legacy projects** :
If your project already uses Vuex, you can keep on using it.
Vuex 3 and 4 will still be maintained. However, it's unlikely to add new functionalities to it. Vuex and Pinia can be installed in the same project. If you're migrating existing Vuex app to Pinia, it might be a suitable option. However, if you're planning to start a new project, we highly recommend using Pinia instead.
i found this one to be more accurate.
https://v2.vuejs.org/v2/guide/components.html#sync-Modifier
only in 2.3.0+ tho.
and honestly it's still not good enough. should simply be a easy option for 'two-way' data binding. so none of these options is good.
try using vuex instead. they have more options for such purpose.
https://vuex.vuejs.org/en/state.html
I would prefer event-driven updates as recommended in the documentation. However, I was limited by the existing ("third-party") component already using props and $emit. This component is my grandchild. The following is my solution (passing value through child using props, sync and computed value with $emit.
Comments are welcome.
Value can be modified in parent and grandchild without error:
Grandchild (simplified third-party component):
<template>
<div v-show="value">{{ value}}</div>
<button #click="closeBox">Close</button>
</template>
<script>
export default {
props: {
value: null
},
methods: {
closeBox() {
this.$emit('update:value', null);
}
}
}
</script>
Child:
<template>
<grandchild-component :value.sync="passedValue" />
</template>
<script>
export default {
props: {
value: null
},
computed: {
passedValue: {
get() {
return this.value;
},
set(newVal) {
this.$emit('update:value', newVal);
}
}
}
}
</script>
Parent:
<template>
<child-component :value.sync="value" />
</template>
<script>
export default {
data() {
return {
value: null,
}
},
// ... e.g. method setting/modifying the value
}
</script>

Ember.js : how to bind a model attribute on a custom component

I would like to integrate a date picker in a form. So I created a custom DateTimePickerView like this :
App.DateTimePickerView = Em.View.extend
templateName: 'datetimepicker'
didInsertElement: ->
self = this
onChangeDate = (ev) ->
self.set "value", moment.utc(ev.date).format("dd/MM/yyyy hh:mm")
#$('.datetimepicker').datetimepicker(language: 'fr', format: 'dd/mm/yyyy hh:ii').on "changeDate", onChangeDate
Here is the template :
<script type="text/x-handlebars" data-template-name="datetimepicker" >
<input type="text" class="datetimepicker" readonly>
</script>
In my form I want to bind this component to an attribute of my model (I am using the RestAdapter) :
<script type="text/x-handlebars" id="post/_edit">
<p>{{view Ember.TextField valueBinding='title'}}</p>
<p>{{view App.DateTimePickerView valueBinding='date'}}</p>
</script>
Everything works fine in apparence : the DateTimePicker is well displayed and the value is set inside the input.
But there is a problem in the effective binding : when I send the form, the post param "date" (corresponding to the attribute) is null.
When I look inside the generated html code I can see the following :
<p>
<input id="ember393" class="ember-view ember-text-field" type="text" value="Event 1">
</p>
<div id="ember403" class="ember-view">
<input type="text" class="datetimepicker" readonly="">
</div>
I am not an expert in the global ember structure, but I guess that the id element is important for the binding. In that case, for my component, the ember id is put to the container of my component and not to the input containing the value. So I guess the problem is here.
So what could be the correct way to make it work ?
I just created a working jsfiddle here ; we can see that the modifications in the title field are taken into account but not the modifications in the DateTimePickerView component.
I guess the problem lies in the fact that you where trying to listen on an event fired from the datetimepicker which is not captured, and thus the model value not set.
To make things more solid you should get the datetimepicker current date value in your doneEditing function, just before saving the model back to the store.
Let me show in code what I mean:
window.App.EventController = Ember.ObjectController.extend(
...
doneEditing: ->
// relevant code line
#get("model").set("date_begin", $('.datetimepicker').data('date'))
#set "isEditing", false
#get("store").commit()
...
)
And here your (working) jsfiddle.
Hope it helps
Edit
After reading your comment I've modified the input field inside your datetimepicker template. Please see here an updated jsfiddle that also initializes the input field of the datetimepicker on edit begin when calling edit.
...
edit: ->
#set "isEditing", true
startDate = #get("model").get("date_begin")
#$(".datetimepicker").data({date: startDate}).datetimepicker("update")
...
You are now safe to remove the onDateChange function and do init and save inside your edit and doneEditing respectively, applying format or whatever.
Edit 2
Reading your last comment, this is how you register customEvents for example in your App.DateTimePickerView:
...
customEvents: {
changedate: "changeDate"
}
...
this way Ember will be aware of your custom events. You can register whatever events you want but notice that the keyname is lowercased and the value must have the event name to listen to camelcased. For more infos on custom events see here.
Please see here for another update jsfiddle with the changeDate custom event registered.
I have finally resolved this problem making some controls when using the moment.js library.
Everything was working fine with the binding process of the custom datetimepickerview.
Here is a working jsfiddle : here
The relevant code is here :
window.App.DateTimePickerView = Ember.View.extend(
templateName: 'datetimepicker'
didInsertElement: ->
#this test is important and was the problem
if #.get 'value'
#$('input').val moment.utc(#.get 'value').format('LLL')
onChangeDate = (ev) =>
date = moment.utc(ev.date).format('LLL')
#.set "value", date
#$('.datetimepicker').datetimepicker(format: 'dd/MM/yyyy', pickTime: false).on "changeDate", onChangeDate
)

Resources