I have a tom-select field works fine with an ajax search. When I select the record to edit the form it does not display any value of what was selected as the location. I am trying to set the default field but nothing has allowed me to have any value show up. It is always blank with the default placeholder text. Does anyone know how to retain what value was saved when editing the field?
select field:
<%= form.select field.name, [], {}, placeholder: 'Type to search', data: {controller: 'ts--search', ts__search_url_value: autocomplete_srs_path }, :required => field.required %>
Stimulus controller:
import { Controller } from "#hotwired/stimulus"
import { get } from '#rails/request.js'
import TomSelect from "tom-select"
export default class extends Controller {
static values = {
url: String
}
connect() {
var config = {
plugins: ['clear_button'],
render: {
option: this.render_option,
item: this.render_option
},
valueField: 'value',
persist: false,
loadThrottle: 300,
load: (q, callback) => this.search(q, callback)
}
new TomSelect(this.element, config);
}
async search(q, callback) {
const response = await get(this.urlValue, {
query: {
q: q
},
responseKind: 'json'
})
if(response.ok) {
callback(await response.json)
} else {
console.log(response)
callback()
}
}
render_option(data, escape) {
return `<div class="bg-gray-50 text-gray-900 dark:bg-gray-700 dark:text-white">${escape(data.text)}</div>`
}
}
controller:
def autocomplete
list = Location.order(:description).where("description like :q", q: "%#{params[:q]}%")
render(json: list.map do |u| { text: u.description, value: u.id } end)
end
Related
I have this logic on changing radio-button selection, if the user made some changing I am showing a message. if he confirm it will enter Onconfirm, else - Onreject.
1 issue -> the change of the radio button happens before the message show.
2 issue -> one reject I want to cancel the choice he made and to undo to his last choise - whice not happenning.
please help me with this!!
radio button
<div class="right" *ngFor="let type of types">
<p-radioButton name="treesDetailsType" [(ngModel)]="oneselectedType" formControlName="selectedType" (onClick)="onChangeType(type,$event)" class="treeDetails" value="{{type.id}}" label="{{type.desc}}" [disabled]="isReadOnly && type.id != data.selectedType"></p-radioButton>
</div>
the function of onclick
onChangeType(type, $event) {
let isFormTouched = this.isFormTouched(type);
if (isFormTouched) {
this.messagingService.showConfirmById(44, () => {
this.onConfirm()
}, () => {
this.onReject($event);
});
}
else
this.onchangedTrue(type); //this set some validators for the choice
}
on reject
#HostListener('click', ['$event']) onReject($event) {
event.stopImmediatePropagation();
//whatever written here its not happens before the change !!!!!
console.log(event);
}
----edited after the perfect suggestion from #Eliseo
askConfirm(value: any) {
let isFormTouched = this.isFormTouched(value);
if (isFormTouched) {
this.messagingService.showConfirmById(44, () => {
this.oneselectedType = value;
this.fg.controls.selectedType.setValue(value);
}, () => {
this.radios.forEach(x => {
x.writeValue(this.oneselectedType);
})
},
);
}
else {
this.oneselectedType = value;
this.onchangedTrue(value);
}
}`
the code work perfectly without the condition
--edited - on get the value from the server and patch it - the radio button lost
There a problem in my code (the another answer). Really I'm not pretty sure the reason, so I create a function like
redraw()
{
const value = this.form.value.type;
this.radios.forEach((x) => {
x.writeValue(value)
});
}
So, my function "ask" becomes like
ask(value: any) {
this.confirmationService.confirm({
message: 'Do you want to choose ' + value + '?',
header: 'Choose Confirmation',
icon: 'pi pi-info-circle',
key: 'positionDialog',
accept: () => {
this.form.get('type').setValue(value);
},
reject: () => {
this.redraw()
},
});
}
This allow me, when change the form, call to the function redraw. If I has a function
getForm(data: any = null) {
data = data || { type: 1, prop: '' };
return new FormGroup({
type: new FormControl(data.type),
prop: new FormControl(data.prop),
});
}
I can do some like
loadData(id: number) {
this.dataService.getData(id).subscribe((res: any) => {
this.form = this.getForm(res);
//it's necesary call to the function this.redraw
this.redraw()
});
}
newData() {
this.form = this.getForm();
//it's necesary call to the function this.redraw
this.redraw()
}
See in the this stackblitz what happens if we don't call to this.redraw() (just comment the lines)
1.-Select "new York" and say that you don't want it
2.-Click the button to load user
As "user" has the type 3 -"new York", the radio buttons looks like that it's not selected.
Yes is an ugly work-around, but for now I can not imagine another solution
Well there're another approach, that is change the value as usually and if we say that we want not the value, return the old value
askAfterChange(value:any)
{
const oldValue=this.form2.value.type;
this.form2.get('type').setValue(value)
this.confirmationService.confirm({
message: 'Do you want to choose ' + value + '?',
header: 'Choose Confirmation',
icon: 'pi pi-info-circle',
key: 'positionDialog',
accept: () => {
},
reject: () => {
this.form2.get('type').setValue(oldValue);
},
});
}
The "key" is split the [(ngModel)] in [ngModel] and (ngModelChanged)
//NOT WORK yet
<p-radioButton ... [ngModel]="selectedType"
(ngModelChange)="askConfirm($event)">
askConfirm(value: any) {
this.confirmationService.confirm({
message: 'Are you sure do you want '+value+'?',
header: 'Delete Confirmation',
icon: 'pi pi-info-circle',
accept: () => {
this.selectedType=value
},
reject: () => {
},
key: "positionDialog"
});
}
Well the problem is that the element still show the value selected How resolved? The first is get our p-radio buttons using ViewChildren, so we are give a template reference variable (the same to all the buttons) see the #radio
<div *ngFor="let type of types" class="p-field-radiobutton">
<p-radioButton #radio ...
(ngModelChange)="ask($event)"
[ngModel]="oneselectedType" ></p-radioButton>
</div>
//get the "radio buttons"
#ViewChildren('radio', { read: RadioButton }) radios!: QueryList<RadioButton>
constructor(private confirmationService: ConfirmationService) { }
ask(value: any) {
this.confirmationService.confirm({
message: 'Do you want to choose this?',
header: 'Choose Confirmation',
icon: 'pi pi-info-circle',
key: 'positionDialog',
accept: () => {
//if accept
this.oneselectedType = value
},
reject: () => {
//else, we loop over all the "radios"
this.radios.forEach(x => {
//and force is checked
x.writeValue(this.oneselectedType);
})
}
});
}
If you're using reactive Forms, you can also use a [ngModel] (ngModelChange) in the way, see that the model is myForm.get('selectedType').value
<p-radioButton ... [ngModel]="myForm.get('selectedType').value"
(ngModelChanged)="askConfirm($event)"
[ngModelOptions]="{standalone:true}"
>
And change in askConfirm
askConfirm(value: any) {
this.confirmationService.confirm({
...
accept: () => {
this.form.get('oneselectedType').setValue(value)
},
reject: () => {
this.radios.forEach(x => {
//and force is checked
x.writeValue(this.form.value.oneselectedType);
})
},
key: "positionDialog"
});
}
a simple stackblitz
Well, In the stackblitz I hard-code the value of the formGroup. Generally we has a service so we can
1.-Define our Form
form=new FormGroup({
selectedCity:new FormControl(),
selectedColor:new FormControl(),
prop:new FormControl()
})
//And in ngOnInit
this.dataService.getData().subscribe(res=>{
this.form.patchValue(res)
})
Or 2.-simple declare our form
form:FormGroup
//and in ngOnInit
use in ngOnInit
this.dataService.getData().subscribe(res=>{
this.form=new FormGroup({
selectedCity:new FormControl(res.selectedCity),
selectedColor:new FormControl(res.selectedColor),
prop:new FormControl(res.prop)
})
})
If we need a default value, we can give the value at first
(the stackblitz has in code this options)
Trying to implement pagination, Initially I'm trying to load datatable with few records, once page loaded trying to click pagination buttons like next or any pagination buttons to update the new set of records. I'm able to get the iStart, iEnd records but unable to update the url for every pagination click. Trying to print the console but function is not calling and console.log is not updated with new params. Could you please suggest me how to do the update the params for API. Here is the sample code,
Sample Demo datatatble is not work with pagination, for verification printing the console for the updated querystring.
ngOnInit(): void {
this.dtOptions = {
processing: true,
destroy: true,
columns: [
{ title: '<input type="checkbox" />' },
{ data: 'index' },
{ data: 'firstname' },
{ data: 'lastname' }
],
infoCallback: (oSettings, iStart, iEnd, iMax, iTotal, sPre) => {
pageStartNo = iStart;
pageEndNo = iEnd;
console.log(pageStartNo, pageEndNo);
// this.loadTable();
}
};
}
loadTable(){
let params = new HttpParams()
.set('param1', '123')
.set('param2', '456')
.set('minNumber', pageStartNo)
.set('maxNumber', pageEndNo);
console.log('params >>>>>>>>>>>>>' + params.toString());
this.http
.get<any[]>(
'https://raw.githubusercontent.com/l-lin/angular-datatables/master/demo/src/data/data.json',
{
params
}
)
.subscribe(response => {
this.persons = response.data;
this.dtTrigger.next();
});
}
HTML code:
<button (click)="loadTable()">Load Table</button>
Sample Demo Stackblitz
If I understand your question correctly, you wanted to apply server-side pagination right?
Here is an official documentation for this.
Add ajax method in dtOptions.
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 10,
serverSide: true,
processing: true,
ajax: (dataTablesParameters: any, callback) => {
console.log('Params', dataTablesParameters);
//If you have different key for page number/size change it
dataTablesParameters.minNumber = dataTablesParameters.start + 1;
dataTablesParameters.maxNumber =
dataTablesParameters.start + dataTablesParameters.length; this.http
.post<any[]>(
'YOUR_API_NAME_HERE',
dataTablesParameters,
{}
)
.subscribe(resp => {
this.persons = resp.data;
//Once API fetched data successfully inform datatable by invoking the callback
callback({
recordsTotal: resp.recordsTotal,
recordsFiltered: resp.recordsFiltered,
data: []
});
});
},
columns: [{ data: 'id' }, { data: 'firstName' }, { data: 'lastName' }]
};
Working Stackbliz Demo
I'm working on a Vue project which has a component for loading content into a modal via an ajax call:
<load-content target="foo"></load-content>
<load-content target="bar"></load-content>
<load-content target="oof"></load-content>
<load-content target="rab"></load-content>
Here's an example template:
<template>
<span class="load-content-wrapper" v-on:click="load">
Click
</span>
</template>
<script>
export default {
name: 'load content',
props: {
target: {
type: String,
required: true
}
},
methods: {
load() {
$('#load-content-modal').modal('show');
this.$store.dispatch('loadContent', this.target);
},
}
};
</script>
Which would trigger this example action:
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
});
},
};
This all works well, except we cannot guarantee that the Ajax call will always return content. Depending on the target it could return 404.
Ideally I want to automatically disable individual load-content components if '/api/fetch-content/' + target isn't available to prevent users from trying to select unavailable content.
What is the correct/ most efficient way to do this?
You should make your "target" field not required and instead add a default value empty string.
And add an "if" condition to your load method. If "target" is empty, it will not proceed.
export default {
name: 'load content',
props: {
target: {
type: String,
default: ''
}
},
methods: {
load() {
if (!this.target) return;
$('#load-content-modal').modal('show');
this.$store.dispatch('loadContent', this.target);
},
}
};
Create a store variable loading and mutate it in your actions as follows:
loading: false
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
commit(setLoading)
});
},
};
Then in muatations ->
setLoading (state, loading) {
state.loading = true
}
Now in your vue file use this store variable and check if it is true then load the component.You may check this created or mounted events of the component.
Option 1
Preemptively load the content, and disable the ones that return an error.
This is what the parent component will look like
<template>
<load-content
v-for="(target, index) in loadedTargets"
:key="index"
target="target"
/>
</template>
<script>
export default {
name: 'load content parent',
data: function() {
return {
targets: [
{link: 'foo', data: null, loaded: false, error: null},
{link: 'bar', data: null, loaded: false, error: null},
{link: 'oof', data: null, loaded: false, error: null},
{link: 'rab', data: null, loaded: false, error: null},
]
}
},
computed: {
loadedTargets() {
return this.targets.filter(t => t.loaded)
}
},
methods: {
load(target) {
const self = this;
$.ajax({
url: '/api/fetch-content/' + target.link,
}).then((data) => {
self.targets[indexOf(target)].data = data
self.targets[indexOf(target)].loaded = true
}).catch((error) => {
self.targets[indexOf(target)].error = error
});
},
},
mounted() {
this.targets.forEach(target => this.load(target))
}
};
</script>
Option 2
Preemptive loading is expensive (and since I don't know how many targets you might have), you could also show success/error in the modal. Proper UX would dictate that an explicit action by the user should lead to a result (i.e. if the user clicks a link, he should either see data in the modal, or an error)
This is what your action will look like:
const actions = {
loadContent ({ commit }, target) {
$.ajax({
url: '/api/fetch-content/' + target,
}).then((data) => {
// Load Modal Window
}).catch((error) => {
// Load Modal Window, and show the error
});
},
};
The Problem
So i am currently trying to implement a color picker inside of a Kendo grid, that will hopefully send the chosen color to my Sql Table. Unfortunately, It doesn't seem as though the Update controller is being reached. I am relatively new to Kendo UI, so there might be some incredibly dumb errors shown.
Questions
I guess my main question would be: How can i call the update method when update is clicked on the grid. Essentially, the color picker and the edit command are showing up in beautiful fashion. I just want to know how i can be sure that the method is being called when 'Update' is clicked, seeing as it is not reaching my controller. Feel free to ask if you need to see more code or perhaps a screen shot.
Code
Config.cshtml ( Grid )
#model IEnumerable<STZN.Models.AGCData.ErrorCode>
#{
ViewBag.Title = "Config";
}
#section HeadContent{
<script src="~/Scripts/common.js"></script>
<script>
$(document).ready(function () {
$("#grid").kendoGrid({
editable: "inline",
selectable: "row",
dataSource: {
schema: {
model: {
id: "error_code",
fields: {
color: { type: 'string' }
}
}
},
transport: {
read: {
type: "POST",
dataType: "json",
url: "#Url.Action("ErrorCodes")"
},
update: {
type: "POST" ,
dataType: "json",
url: "#Url.Action("UpdateErrorCodes")",
}
}
},
columns: [
{ command : [ "edit" ] },
{
field: "error_code", title: "Error Code",
},
{
field: "error_description", title: "Error Description"
},
{
field: "color",
width: 150,
title: "Color",
template: function (dataItem) {
return "<div style = 'background-color: " + dataItem.color + ";' </div>"
},
editor: function (container, options) {
var input = $("<input/>");
input.attr("color",options.field);
input.appendTo(container);
input.kendoColorPicker({
value: options.model.color,
buttons: false
})
},
}
]
});
});
</script>
}
Update Controller
public JsonResult UpdateErrorCodes(ErrorCode model)
{
using (var db = new AgcDBEntities())
{
db.Entry(model).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
db.Configuration.ProxyCreationEnabled = false;
var data = db.ErrorCodes.Where(d => d.error_code == model.error_code).Select(x => new
{
error_code = x.error_code,
description = x.error_description,
color = x.color,
});
return new JsonResult()
{
JsonRequestBehavior = System.Web.Mvc.JsonRequestBehavior.AllowGet,
};
}
}
I actually managed to fix my issue by adding an additional input attribute to my editor function in the "color" field. It looks like this:
input.attr("data-bind","value:" + options.field);
There are still some present issues (unrelated to the fix/server update) , but as far as updating to the server, It work's as intended.
I have a knockout viewModel and am wiring up jQuery Validation for it. One of the values, code, I want a remote check to ensure it's not already in use. The problem is that in my method for the remote validation, the self.code() call is returning the old value instead of the new one.
My Validate code (note I also tried a "more direct" method of getting the value, to no avail - same result):
form.validate({
rules: {
'plandetail-code': {
required: true,
remote: {
url: '/Plans/ValidatePlanCode',
type: 'POST',
data: {
id: self.id(),
code: self.code() //form.find('[name="plandetail-code"]').val()
}
}
},
'plandetail-name': "required"
}
});
Relevant Html:
<div class="form-group">
<label for="plandetail-code">Code</label>
<input type="text" name="plandetail-code" data-bind="textInput: code" class="form-control" />
</div>
My controller action is simple, but note that code always comes through as the original value:
[HttpPost]
public string ValidatePlanCode(int? id, string code) {
return _service.ValidatePlanCode(id, code) ? "true" : "false";
}
And here's my viewmodel: I run the form.Validate({}) before applying bindings (tried putting that after as well), and in the saveChanges method I check form.valid():
function PlanDetailVM(model) {
var self = this;
self.originalModel = model;
self.form = $('#pgPlan-plan-detail-form');
self.id = ko.observable(model.ID);
self.active = ko.observable(model.Active);
self.code = ko.observable(model.Code);
self.name = ko.observable(model.Name);
self.notes = ko.observable(model.notes);
self.dirty = ko.computed(function () { return isDirty(); });
self.save = function () { saveChanges(); }
self.cancel = function () { cancelChanges(); }
ko.applyBindings(self, document.getElementById('pgPlan-detail-container'));
initValidation(self.form);
return self;
function initValidation(form) {
form.validate({
rules: {
'plandetail-code': {
required: true,
remote: {
url: '/Plans/ValidatePlanCode',
type: 'POST',
data: {
id: self.id(),
code: self.code() //form.find('[name="plandetail-code"]').text()
}
}
},
'plandetail-name': "required"
}
});
}
function isDirty() { ... }
function saveChanges() {
if (!self.form.valid()) {
return;
}
// ajax snipped
}
function cancelChanges() { ... }
}
Repro:
Load initial view, Code has value AAAA
Change Code to BBBB
Observe controller action called
Controller action code param = AAAA
I'm unsure why I can't get the latest value from the text input. Am I missing something? Thanks
rules is an object which is evaluated immediately, so the data object will get created with default values if you use self.id() (since it returns value not function)
so you need to use it as functions
form.validate({
rules: {
'plandetail-code': {
required: true,
remote: {
url: '/Plans/ValidatePlanCode',
type: 'POST',
data: {
id: self.id, // function evaluated at runtime
code: self.code
}
}
},
'plandetail-name': "required"
}
});