Element hidden but its validation tips still showes - iview

I'm using VUE 2.5.1 & iView 4.0.0. There are 2 forms in a page of my project, and I use v-if property to switch them. The problem is after I re-assign the value of form_data (the new one does not have property 'usage') and set this.met_type to 3, the 'Item' input showes, but a line of red text 'usage is required' also showes under it. The 'Usage' input is already hidden, why does its validation tips still show? Is there a way to avoid this wrong message?
<template>
<div v-if="met_type==2">
<Form :rules="ruleCustom" :model="form_data">
<Row>
<Col span="12">
<FormItem label="Usage" prop="usage" required>
<Input :value="form_data.usage" readonly />
</FormItem>
</Col>
</Row>
<!-- other code omitted -->
</Form>
</div>
<div v-if="met_type==3">
<Form :rules="ruleCustom" :model="form_data">
<Row>
<Col span="12">
<FormItem label="Item" prop="item" required>
<Input :value="form_data.item" readonly />
</FormItem>
</Col>
</Row>
<!-- other code omitted -->
</Form>
</div>
<!-- other code omitted -->
</template>
<script>
export default {
methods: {
loadData: function(met_type) {
dataUtils.fetch(this.met_id, met_type, (retVal) => {
// Fetch data
// whe met_type == 3, form_data does not have property 'usage'
this.form_data = retVal.data;
this.met_type = met_type;
});
}
}
}
</script>

After I put these two forms into seperate template files, the problem is solved. But I still don't understand why this takes effective.
// ComponentA.vue
<template>
<Form :rules="ruleCustom" :model="form_data">
<Row>
<Col span="12">
<FormItem label="Usage" prop="usage" required>
<Input :value="form_data.usage" readonly />
</FormItem>
</Col>
</Row>
<!-- other code omitted -->
</Form>
</template>
// ComponentB.vue
<template>
<Form :rules="ruleCustom" :model="form_data">
<Row>
<Col span="12">
<FormItem label="Item" prop="item" required>
<Input :value="form_data.item" readonly />
</FormItem>
</Col>
</Row>
<!-- other code omitted -->
</Form>
</template>
// The main component file
<template>
<div v-if="met_type==2">
<ComponentA>
</ComponentA>
</div>
<div v-if="met_type==3">
<ComponentB>
</ComponentB>
</div>
<!-- other code omitted -->
</template>
<script>
export default {
methods: {
loadData: function(met_type) {
dataUtils.fetch(this.met_id, met_type, (retVal) => {
// Fetch data
// whe met_type == 3, form_data does not have property 'usage'
this.form_data = retVal.data;
this.met_type = met_type;
});
}
}
}
</script>

Related

How to configure Croppie for vue 3 in laravel 8?

In a vue page I added a ImageUpload component, following this tutorial: https://www.youtube.com/watch?v=kvNozA8M1HM
It works more or less. No errors, but it only shows my image and a ruler. I can zoom using the ruler, but it does not show boundaries, circle etc. It is completely different from the example in the tutorial...
It is buggy, so to say.
<template>
<div class="Image-Upload-wrapper Image-upload">
<p>
This is image upload wrapper component
</p>
<div id="croppie"> </div>
</div>
</template>
<script>
// uitleg over Croppie::https://www.youtube.com/watch?v=kvNozA8M1HM
import Croppie from 'croppie';
export default {
props:[
'imgUrl'
],
mounted(){
this.image = this.imgUrl // als component is mounted, this.image definieren, en daartmee croppie in..
this.setUpCroppie()
},
data(){
return{
image:null,
croppie:null
}
},
methods:{
setUpCroppie(){
let el = document.getElementById('croppie');
this.croppie = new Croppie(el, {
viewport:{width:200,height:200,type:'circle'},
boundary:{ width:220,height:220},
showZoomer:true,
enableOrientation: true
});
this.croppie.bind({
url:this.image
});
}
}
}
</script>
Is placed in parent as follows:
template>
<div>
<h2>Cards 1</h2>
<div class="form-group">
<input type="text" class="form-control" :placeholder="card.title" v-model="card.title">
</div>
<div class="form-group">
<textarea class="form-control" :placeholder="card.description" v-model="card.description">
</textarea>
</div>
<div id="preview" v-if="url" >
<h4> Preview</h4>
<ImageUpload :imgUrl="url"></ImageUpload>
<!-- <img class="img-circle" style="width:150px" :src="url" /> -->
</div>
<input type="file" v-on:change="onFileChange" ref="fileUpload" id="file_picture_input">
<button #click="editCard(currentSpelerCard)" class="btn btn-warning m-1" style="width:100px;color:black"> Save Card </button>
</div>
</template>
In my package.json I have:
"croppie": "^2.6.5",
in webpack mix I have:
mix.js('resources/js/app.js', 'public/js')
mix.vue();
mix.sass('resources/sass/app.scss', 'public/css');
I have a feeling that something is wrong with the installation of croppie.
Does anyone know what could be the problem here?
Googloing on I found the remark:
"#Since this package also depends on croppie.css you have to import it in your app.js import 'croppie/croppie.css'"
I added the following line to bootstrap.js:
import 'croppie/croppie.css';

how can i render an array using different styling according to a specific property in this case message.senderEmail

function AlignFeature(){
const [allMessages, setAllMessages] = useState([]);
useEffect(()=>{
MessageService.getAllMessages().then(res=>{
setAllMessages(res.data);
});
}, []);
return (
<Container>
<Row>
<Col md={{ span: 6, offset: 0}}>
<div>
<div><Button color='warning'
onclick={MessageService.deleteChat}>
delete chat</Button></div>
<div className='chat'>
<div className='sentMes'>{
allMessages.map(message =>
<div key = {message.id}
className='sentMessages'>
<p
> {message.message} </p>
</div>
)}
</div>
</div>
<div style={{position:'static'}}>
<MessageForm />
</div>
</div>
</Col>
<Col md={{ span: 3, offset: 0}}> <UploadFiles /></Col>
</Row>
</Container>
);
}
export default AlignFeature;
properties of message are(id, message, receiverEmail, senderEmail)
after the map function i want to render message.message either left or right by checking if message.receiverEmail compares to an email stored in localStorage. if message.receiverEmail == localStorage.getItem('email') display right else display left
There are two ways of going about this problem my sources are Composition vs Inheritance and this question about updating parent state in react. This is also a good resource for learning about state.
From your question you are wanting to compare against data gained later and update the UI of a previously generated component based on that data. With react everything is based upon updating the state which updates the presented information hooks just provide a different way of doing this.
An example that fits into your current way of doing things (hooks) would be to pass an update function that will update the parent state to a lower level component.
function AlignFeature() {
const [allMessages, setAllMessages, localMessages, setLocalMessages] = useState([]);
useEffect(() => {
MessageService.getAllMessages().then(res => {
setAllMessages(res.data);
});
}, []);
return (
<Container>
<Row>
<Col md={{ span: 6, offset: 0 }}>
<div>
<div><Button color='warning'
onclick={MessageService.deleteChat}>
delete chat</Button></div>
<div className='chat'>
<div className='sentMes'>{
allMessages.map(message => {
if (localMessages.some(m => message.receiverEmail == m.receiverEmail)) {
// when email matches local
<div key={message.id} className='sentMessages'>
<p style={{ float: 'right' }}> {message.message} </p>
</div>;
} else {
// when email does not match local
<div key={message.id} className='sentMessages'>
<p> {message.message} </p>
</div>
}
}
)}
</div>
</div>
<div style={{ position: 'static' }}>
<MessageForm />
</div>
</div>
</Col>
<Col md={{ span: 3, offset: 0 }}> <UploadFiles updateLocalMessages={(lMessages) => setLocalMessages(lMessages)} /></Col>
</Row>
</Container>
);
}
export default AlignFeature;
<UploadFiles/> will need to assign the local emails or a list of email addresses using the passed in method which needs to be defined.
Note: It may be a good idea to consider case comparisons when comparing email addresses by converting them to both lower or upper case to ensure they match if that fits your use case.

Problem with making dynamic form based on table field in Vue Js

I got the problem in making program for my company, the concept is you can input mark (nilai) for every sector based on kriteria of the mark you had to fill and it has 5 kriteria. the problem is I have to separate the kriteria based on the kriteria name and when I try the array cant be shown
<template>
<div class="space-x-4 sm:-my-px sm:ml-1 sm:flex">
<jet-button #click="getKriteria1">P1</jet-button>
<jet-button #click="getKriteria2">P2</jet-button>
<jet-button #click="getKriteria3">P3</jet-button>
<jet-button #click="getKriteria4">P4</jet-button>
<jet-button #click="getKriteria5">P5</jet-button>
</div>
<div class="mt-4">
<div class="space-x-4 sm:-my-px sm:ml-1 sm:flex" v-if="session === 1" :v-for="(kriteria, index) in kriteria1.data" :key="kriteria.id">
<label>{{kriteria.sub_kriteria}} : </label>
<jet-input type="number" class="mt-1 block w-3/4" placeholder="Nilai"
ref="nilai"
v-model="form.nilai[index]"
#keyup.enter="create" />
<jet-input-error :message="form.errors.nilai" class="mt-2" />
</div>
</div>
</template>
can you guys help me why the kriterias i get cant be show, in the console i can see the kriteria is read and has field but when i try to call it with props it cant be shown and cant be convert to kriteria1
<script>
props: {
penilaians: Array,
kriterias:Array,
},
data() {
return {
session:1,
kriteria1:[],
kriteria2:[],
kriteria3:[],
kriteria4:[],
kriteria5:[],
form:this.$inertia.form({
nilai:[],
foto:[],
status:[],
rekomendasi:[]
}),
}
},
methods:{
getKriteria1(){
this.session = 1;
this.kriteria1 = kriterias;
for(let index=0;index<=kriterias.length;index++){
if(kriterias[index].nama === 'P1'){
this.kriteria1[index].id = kriterias[index].id;
this.kriteria1[index].nama = kriterias[index].nama;
this.kriteria1[index].sub_kriteria = kriterias[index].sub_kriteria;
}
}
},
}
</script>
i erase the component and import in purpose because it will not affect the question i think
the main idea is to filter the 'kriteria' field to spesific values so i made the filter with getKriteria 1 so i can get 'nilai' form for the 'kriteria'
method:{
getKriteria1(){
this.session = 1;
var kriteria = this.kriterias;
var j=0;
for(var i=0;i<= kriteria.length;i++){
if(kriteria[i].nama == 'P1'){
this.kriteria1[j] = kriteria[i];
j++;
}
}
},
}
this is the jetstream template of the program :
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="space-x-4 sm:-my-px sm:ml-1 sm:flex" :v-on:show ="getPenilaianId()">
<jet-button v-on:click.prevent="getKriteria1()" >P1</jet-button>
<jet-button v-on:click.prevent="getKriteria2()" >P2</jet-button>
<jet-button v-on:click.prevent="getKriteria3()" >P3</jet-button>
<jet-button v-on:click.prevent="getKriteria4()" >P4</jet-button>
<jet-button v-on:click.prevent="getKriteria5()" >P5</jet-button>
</div>
<div class="mt-4">
<div v-if="session === 1">
<div v-for="item in kriteria1" :key="item.id" :v-on:show="getKriteriaId(item.id-1)">
<div v-if="item === null"> Tidak ada Kriteria </div>
<label>{{item.nama}} - {{item.sub_kriteria}}</label>
<jet-input type="number" class="mt-1 block w-3/4" placeholder="Nilai"
ref="nilai"
v-model="form.nilai[item.id-1]"
#keyup.enter="create" />
<jet-input-error :message="form.errors.nilai" class="mt-2" />
</div>
</div>
</div>

When Jcrop handle more than one image, the size is incorrect

1,
2,
Question:When do we change images dynamically ?
Answer: When user asynchronously upload images, such as using ajaxfileupload.js.
when a user upload one image, and want to upload another one.
If he uses Jcrop, the later images are all shown in size of the first image.
even though he uses following jQuery code to change the size of the new image, it`s still in wrong size.
$("#target").attr("width","400");
$("#target").attr("height","250");
Does anyone know how to use Jcrop and show each images in its real size? I mean when the images is dynamically changed.
Thanks a lot!
The souce code is compressed into a zip file, they are all font-end code, so you may open it once it`s unzipped, please open "crop.html"
you may download the zip file (214KB) here:
download
the content of "crop.html" is:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="js/jquery.min.js"></script>
<script src="js/jquery.Jcrop.js"></script>
<script type="text/javascript">
jQuery(function($) {
var jcrop_api;
initJcrop();
$('#coords').on('change', 'input', function(e) {
var x1 = $('#x1').val(),
x2 = $('#x2').val(),
y1 = $('#y1').val(),
y2 = $('#y2').val();
jcrop_api.setSelect([x1, y1, x2, y2]);
});
function initJcrop() {
$('#target').Jcrop({
onChange: showCoords,
onSelect: showCoords,
onRelease: clearCoords
}, function() {
jcrop_api = this;
});
};
$('#changeToWiderPicture').click(function(e) {
jcrop_api.destroy();
$("#target").attr("src", "img/2.jpg");
$("#target").attr("width", "400");
$("#target").attr("height", "250");
$("#normalPicture").attr("src", "img/2.jpg");
$("#normanPicureIntroduction").html("Original picure:400X250");
$('#changeToWiderPicture').hide();
$('#changeBack').show();
initJcrop();
return false;
});
$('#changeBack').click(function(e) {
jcrop_api.destroy();
$("#target").attr("src", "img/1.jpg");
$("#normalPicture").attr("src", "img/1.jpg");
$("#normanPicureIntroduction").html("Original picure:250X400");
$('#changeBack').hide();
$('#changeToWiderPicture').show();
initJcrop();
return false;
});
});
function showCoords(c) {
$('#x1').val(c.x);
$('#y1').val(c.y);
$('#x2').val(c.x2);
$('#y2').val(c.y2);
$('#w').val(c.w);
$('#h').val(c.h);
};
function clearCoords() {
$('#coords input').val('');
};
</script>
<link rel="stylesheet" href="css/main.css" type="text/css" />
<link rel="stylesheet" href="css/demos.css" type="text/css" />
<link rel="stylesheet" href="css/jquery.Jcrop.css" type="text/css" />
</head>
<body>
<table>
<tr>
<td>
<img src="img/1.jpg" id="target" />
<br>Picture with Jcrop attached
</td>
<td width=40%></td>
<td>
<img src="img/1.jpg" id="normalPicture" />
<br>
<span id='normanPicureIntroduction'>Original picure:250X400</span>
</td>
</tr>
</table>
<form id="coords" class="coords">
<div class="inline-labels">
<label>X1
<input type="text" size="4" id="x1" name="x1" />
</label>
<label>Y1
<input type="text" size="4" id="y1" name="y1" />
</label>
<label>X2
<input type="text" size="4" id="x2" name="x2" />
</label>
<label>Y2
<input type="text" size="4" id="y2" name="y2" />
</label>
<label>W
<input type="text" size="4" id="w" name="w" />
</label>
<label>H
<input type="text" size="4" id="h" name="h" />
</label>
</div>
</form>
<div style="padding-top:20px;">
<button id="changeToWiderPicture" class="btn btn-mini">changeToWiderPicture</button>
<button id="changeBack" class="btn btn-mini" style="display:none;">changeBack</button>
</div>
</body>
</html>
I find a solution from another answer on another question:
use -
$('#image').removeAttr('style');
Or simply overwrite the corresponding CSS style, for example -
$("#target").css({"width":"400px" ,"height":"250px"});
please be aware:
CSS style has a higher priority than the attribute, so sometimes, you find your change does not apply, for example -
$("#target").attr("width","400");
$("#target").attr("height","250");
So, for safer, user CSS style instead of an attribute.

how to update 2 text fields on click of a search button in the same form by fetching data from Grails Controller using AJAX in Grails 2.1.0

I have a controller(SearchController) which fetches data on clicking a search button (inside myview.gsp) i want to populate 2 text fields present in the same myview.gsp with the data i have in my controller (SearchController) .I know i need to do ajax calling because i want to populate only 2 fields and dont want to reload the page which contains other valuable information . How can i achieve it please guide me
here is my Controller
def searchItem() {
def itemFound = MyService.searchP20Code(params["item"])
def resultMap = [:]
if (itemFound!=null)
{
resultMap.put("CODE",itemFound[1])
resultMap.put("DESC",itemFound[2])
}
println "Result:"+resultMap
session.setAttribute("searchResult", resultMap)
render(view: myview" )
}
myview.gsp
myview.gsp
<div class="leftPanel filter-label">
<a href="#" class="tt"> <g:message
code="Normal.label.Code" />
</a>
</div>
<div class="rightPanel filter-field-wrapper">
<input type="text"
name="Code" maxlength="30"
value=""
id="i_code" />
</div>
</div>
<div class="formLables">
<div class="leftPanel filter-label">
<a href="#" class="tt"> <g:message
code="Normal.label.Description" />
</a>
</div>
<div class="rightPanel filter-field-wrapper">
<input type="text"
name="Desc" maxlength="30"
onkeyup="fieldMaxlength('material_code')" value=""
id="i_description" />
</div>
</div>
<g:actionSubmit
value="${message(code:'Normal.button.search', default: 'Search Code')}"
action="searchItem" />
please help my how can i update only these two text fields in myview.gsp onclicking search button using ajax .I am using grails 2.1.0
Here you can find a detailed example with Grails build-in Ajax support.
You can also use JQuery (example here)
EDIT:
Your controller needs to look like this:
import grails.converters.JSON
class ExampleController {
....
def searchItem() {
def itemFound = MyService.searchP20Code(params["item"])
def resultMap = [:]
if (itemFound!=null) {
resultMap.put("CODE",itemFound[1])
resultMap.put("DESC",itemFound[2])
}
println "Result:"+resultMap
//session.setAttribute("searchResult", resultMap)
render(resultMap as JSON )
}
}
and your gsp
<head>
...
<g:javascript plugin="jquery" library="jquery" src="jquery/jquery-{WRITE_YOUR_VERSION_HERE}.js"/>
<script>
function callAjax(e){
e.preventDefault();
$.ajax({
url: "example/search",
type:"post",
dataType: 'json',
success: function(data) {
console.log(data);
$("input[name='Code']").val(data.CODE);
$("input[name='Desc']").val(data.DESC);
}
});
}
</script>
...
</head>
<body>
....
<input type="submit" value="Call Ajax Function" onclick="callAjax()">
....
</body>

Resources