Kendo DropDown filter and show in GridView - kendo-grid

I have filter with dropdown cities. I wanna that when I choose city from dropdown lis and click button search to show data in gridview with this city.
First question
1) How to get value from dropdown and pass to button and call controller?
2) I tried without dropdown, when populate value directly in Read method but nothing, my gridview is empty.
This is my code
Partial View "Filter", View with gridview and method in controller that populate gridview.
#{
ViewBag.Title = "Filter";
}
<div class="filter-all">
<div class="filter-dropdown">
<div class="filter-part">
<div class="custom-label-div">
City:</div>
<div class="defaultSize">
#(Html.Kendo().DropDownList()
.Name("City")
.HtmlAttributes(new { style = "width:250px" })
.DataTextField("CityName")
.DataValueField("CityID")
.OptionLabel("...")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetCities", "Filter");
});
})
)
</div>
</div>
</div>
<div class="filter-buttons">
<div class="button-filter-div">
<input type="button" value="Search City" onclick="location.href='#Url.Action("Index", "MFS3")'" class="k-button" style="width: 80px"/>
<input type="button" value="Cancel" onclick="location.href='#Url.Action("Index", "Preview")'" class="k-button" style="width: 80px"/>
</div>
</div>
</div>
#model IEnumerable<MFS_App.Models.MFS3ViewModel>
<div class="right-content shadow">
<div class="preview-header">
Preview Reports</div>
<div class="preview-content">
#Html.Partial("_Filter")
</div>
</div>
<div class="parent-preview-content shadow">
<div class="child-preview-content">
#Html.Partial("_ReportsGridView")
<div class="mfs-title">
<div class="filter-preview-div">
#(Html.Kendo().Grid(Model)
.Name("GridMFS3")
.Columns(columns =>
{
columns.Bound(p => p.FirstName).HtmlAttributes(new { style="width:50px"});
columns.Bound(p => p.LastName).HtmlAttributes(new { style ="width:70px"});
columns.Bound(p => p.Address).HtmlAttributes(new { style = "width:80px"});
columns.Bound(p => p.Mail).HtmlAttributes(new { style = "width:100px" });
columns.Bound(p => p.County).HtmlAttributes(new { style = "width:70px" });
columns.Bound(p => p.City).HtmlAttributes(new { style = "width:50px" }); columns.Command(c => c.Edit());
})
.DataSource(source =>
{
source.Server()
.Model(model => model.Id(m => m.MFS3_ID))
.Read(read => read.Action("GetMFS", "MFS3", new { cityID = 251} ))
.Update(update => update.Action("Update", "MFS3"));
})
.Editable(editable => editable.Mode(GridEditMode.PopUp))
.Selectable(selectable => selectable.Mode(GridSelectionMode.Multiple))
.Pageable()
.Resizable(resize => resize.Columns(true))
.HtmlAttributes(new { style = "width: 1850px" })
)
private IEnumerable<MFS3ViewModel> GetMFS3(int cityID)
{
return HelperClass.dbUp.TBL_MFS_MFS3_Input.Select(p => new MFS3ViewModel
{
CITYID = p.CITIYID,
MFS3_ID = p.MFS3_ID,
FirstName = p. FirstName,
LastName = p. LastName,
p.Address = p. p.Address,
.Mail = p. .Mail,
County = p. County,
City = p. City,
}).Where(p => p.CITYID == cityID);
}

I resolved this via jQuery and added parameter in my Index method
$('#btnSearch').click(function () {
var cityID = $("#City").data("kendoDropDownList").value();
document.location = "/MFS3/Index?cityId=" + cityID;
});
public ActionResult Index(int cityId)
{
return View(GetMFS3(cityId));
}

Related

how to remove picture from data using api in react

so i am trying to learn api and doing this cool project. so what i have is that i got photos data from this api website:https://jsonplaceholder.typicode.com/
so basically i have a buttom that will add thumbnail data from api based on the albumId in the posts section and when i press on the image it will send the url of the image to a function which will load it on top.
so what i want is to be able to remove the picture up top to have like this red x next to it and when i press it it will be deleted from the upper section and also from the posts section.
my code so far:
const DataFetching = () =>{
const [photo,setPhoto] = useState([])
const[albumId,setAlbumId] = useState(1)
const[albumIdFromButtonClick,setAlbumAIdFromButtonClick] = useState(1)
useEffect(()=>{
axios.get(`https://jsonplaceholder.typicode.com/photos?albumId=${albumIdFromButtonClick}`)
.then(res =>{
console.log(res)
setPhoto(res.data)
})
.catch(err =>{
console.log(err)
})
},[albumIdFromButtonClick])
const handleClick = () => {
setAlbumAIdFromButtonClick(albumId)
}
const [Url,setUrl] = useState()
const showPicture = (url) => {
setUrl(url)
}
return(
<div>
<div><img src={Url}></img></div>
<div>Posts</div>
<p>enter album Id to see the magic</p><input type="text" value={albumId} onChange={e => setAlbumId(e.target.value)}></input>
<button type="button" onClick={handleClick}>Fetch Album</button>
<div className="container">
<div>{photo.map(p =>
<div key={p.id}>
<img src={p.thumbnailUrl} thumbnailUrl={p.thumbnailUrl} onClick={() => showPicture(p.url)}></img>
<div>{p.title}</div>
</div>
)}</div>
</div>
{/* {
photos.map(photo => <div key={photo.id}>{photo.title}</div>)
} */}
</div>
)
}
export default DataFetching;
Ok so I expect you have an button to delete the post image.
When you are clicking in the button you are calling deleteImageFromAlbum() method for example.
So the code will be :
const deleteImageFromAlbum = photoID => {
setUrl('')
setPhoto(photos.filter(photo => photo.id !== photoID));
}
the filter method update the array and takes all photos which the id is not equal to the targeted photos.
You create a function to handle delete action. It returns new photo with removed record that matches id
const handleDelete = id => {
setUrl(null);
setPhoto(prevState => prevState.filter(x => x.id !== id));
};
and you then you add this button somewhere inside rendered list
<div onClick={() => handleDelete(p.id)}>delete</div>
In case you want just delete open prevew:
const hanldeDeletePreview = url => {
setUrl(null);
setPhoto(prevState => prevState.filter(x => x.url !== url));
};
and add button somewhere like this
<div>
<img src={Url} />
<div onClick={() => hanldeDeletePreview(Url)}>delete</div>
</div>
It filters you list by url so it if you have list with matching urls then it will delete all of them. If you want to delete it based on id then you should redo your url preview logic to use id instead.
Here is the rewrite that uses id to show preview
const [previewId, setPreviewId] = useState(null);
const showPicture = id => {
setPreviewId(id);
};
const handleDelete = id => {
setPhoto(prevState => prevState.filter(x => x.id !== id));
};
const hanldeDeletePreview = id => {
setPreviewId(null);
handleDelete(id);
};
const previewHelper = () => {
const selected = photo.find(x => x.id === previewId);
return selected ? (
<>
<img src={selected.url} />
<div onClick={() => hanldeDeletePreview(previewId)}>delete preview</div>
</>
) : null;
};
return (
<div>
<div>{previewHelper()}</div>
<div>Posts</div>
...
<img
src={p.thumbnailUrl}
thumbnailUrl={p.thumbnailUrl}
onClick={() => showPicture(p.id)}
/>

Open and close expandable button in list view

I have a list of Messages that you should be able to click and expand more for info. At the moment, my implementation expands/collapses all messages by clicking on any message.
I tried using the code below:
this.state = {
activeIndex:0,
isExpandable:false
}
And applying the condition as:
{!this.state.isExpandable && this.state.activeItem === i} to the map() where i was retrieving properties of each object.
image of buttons in collapsable state
image of buttons in expanded state
import React, { Component } from 'react'
import { Card, Feed } from 'semantic-ui-react'
import { Input } from 'react-input-component';
import { Collapse, Button} from 'reactstrap';
import styled from 'styled-components';
function searchingForName(search){
return function(x){
return x.firstName.toLowerCase().includes(search.toLowerCase()) || x.lastName.toLowerCase().includes(search.toLowerCase()) || !search ;
}
}
class Home extends Component {
constructor(props){
super(props);
this.state = {
results:[],
search:'',
collapse:false,
newSearch:'',
tags:[],
isExpandable:false,
activeIndex:0
}
this.onchange = this.onchange.bind(this);
this.toggle = this.toggle.bind(this);
this.inputKeyDown = this.inputKeyDown.bind(this);
// this.handleKeyPress = this.handleKeyPress.bind(this);
}
onchange = e => {
console.log(this.state.search)
this.setState({search:e.target.value});
}
// handleKeyPress = e => {
// if(e.key === 'Enter'){
// this.setState({newSearch: e.target.value});
// }
// }
inputKeyDown = (e) => {
const val = e.target.value;
if(e.key === 'Enter' && val){
if (this.state.tags.find(tag => tag.toLowerCase() === val.toLowerCase())) {
return;
}
this.setState({tags: [...this.state.tags,val]});
this.tagInput.value=null;
}
}
toggle(){
this.setState({collapse: !this.state.collapse});
}
componentDidMount(){
fetch('https://www.hatchways.io/api/assessment/students')
.then(res => res.json())
.then(data => {
console.log(data.students);
this.setState({results:data.students})
}).catch(err => {
console.log(err);
});
}
render() {
return (
<div>
<Card style={{'marginTop':'40px','width':'520px','marginRight':'auto','marginLeft':'auto'}}>
<Card.Content>
<Input
style={{'width':'519px'}}
placeholder="Search by name..."
onChange={this.onchange}
/>
<Input
style={{'width':'519px'}}
placeholder="Search by tags..."
onChange={this.onchange}
/>
{this.state.results.length ?
this.state.results.filter(searchingForName(this.state.search)).map((value,i) => (
<Feed>
<Feed.Event style={{'margin':'10px'}}>
<Image>
<Feed.Label image={value.pic} />
</Image>
<div style={{'float':'right'}}>
{!this.state.collapse ?
<Button onClick={this.toggle}>+</Button>
: <Button onClick={this.toggle}>-</Button>}
</div>
<Feed.Content style={{'textAlign':'center','marginBottom':'10px'}}>
<Feed.Summary><strong>{value.firstName.toUpperCase()} {value.lastName.toUpperCase()}</strong></Feed.Summary>
<Feed.Summary>Email: {value.email}</Feed.Summary>
<Feed.Summary>Company: {value.company}</Feed.Summary>
<Feed.Summary>Skill: {value.skill}</Feed.Summary>
<Feed.Summary>Average : {value.grades.map((x,i,arr)=> {
return x/arr.length;})
.reduce((a,b) => {
return a + b;
}) + "%"}
</Feed.Summary><br />
<Collapse isOpen={this.state.collapse}>
<Feed.Summary>
{Array.isArray(value.grades) && value.grades.map(val => {
return <div>Test {value.grades.indexOf(val)} : {parseFloat(val) + "%"}</div>
})}
</Feed.Summary><br />
{this.state.tags.map((tag,index) => (
<div>
<span className="addTag"key={index}>{tag}</span>
</div>
))}<br />
<input
type="text"
onKeyDown={this.inputKeyDown}
ref={c => { this.tagInput = c; }}
placeholder="add a tag..."
/>
{/* <div>{this.state.newSearch}</div><br />
<Input
style={{'width':'200px'}}
placeholder="add a tag..."
value={this.state.newSearch}
onKeyPress={this.handleKeyPress}
/> */}
</Collapse>
<hr/>
</Feed.Content>
</Feed.Event>
</Feed>
)) : ''}
</Card.Content>
</Card>
</div>
)
}
}
const Image = styled.div`
border: 1px solid #001;
border-radius: 60px;
overflow:hidden;
padding:18px;
height:90px;
width: 90px;
margin-top:30px;
margin-right:auto;
margin-left:auto;
margin-bottom:20px;
`
export default Home;
What is causing them all to expand/collapse at once and how can i change that to only expand/collapse the button is clicked?
Your main problem is that you don't have isOpened card for every student. You can only open all or none with one single collapse state. I have updated your code with solution here:
https://codesandbox.io/s/peaceful-kapitsa-tr9yn
I have changes toggle function, which takes index as parameter and updates single students card status - isOpened true or false.
toggle(index) {
const results = this.state.results.map((item, idx) => {
if (index === idx) {
return {
...item,
isOpened: !item.isOpened
};
}
return item;
});
this.setState({ results });
}
When you load all students data from API endpoint, you have to map through all items and add default isOpened state (by default I've added false - closed).
componentDidMount() {
fetch("https://www.hatchways.io/api/assessment/students")
.then(res => res.json())
.then(data => {
console.log(data.students);
const results = data.students.map(student => {
return {
...student,
isOpened: false
};
});
this.setState({ results });
})
.catch(err => {
console.log(err);
});
}
In render() method I have updated every item to check not this.state.collapse, but student.isOpened on Collapse component and toggle button.
Toggle button
<div style={{ float: "right" }}>
{!value.isOpened ? (
<Button onClick={() => this.toggle(i)}>+</Button>
) : (
<Button onClick={() => this.toggle(i)}>-</Button>
)}
</div>
Collapse component
<Collapse isOpen={value.isOpened}>
...
</Collapse>

How to create nested grid in kendo?

I am trying to create a nested grid that will fetch data and populate into 2 grid, parent and a child.
Child grid has only 1 parent
Child grid limited to adding 16 rows
Removing the parent row will re-fetch data from server
Child grid is always open
It similar to this http://dojo.telerik.com/UqURE
But finally should look like this https://photos.app.goo.gl/X6fcZ79779bgjrRZ8
You need to have two kendo grid in your view and two javaScript Method to gets your additional data az method parameters :
The parent grid and its child should be something like this :
<div class="row">
<div class="col-md-12">
#(Html.Kendo().Grid<ProjectName.Models.ViewModel.TasksStaticViewModel>()
.Name("grid")
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("ReadActionName", "ControllerName").Data("additionalData"))
)
.Columns(columns =>
{
columns.Bound(c => c.Column1).HtmlAttributes(new { style = "text-align:center; overflow: hidden; white-space: nowrap;" }).HeaderHtmlAttributes(new { style = "text-align:center;" }).Width(130);
columns.Bound(c => c.Column2).HtmlAttributes(new { style = "text-align:center; overflow: hidden; white-space: nowrap;" }).HeaderHtmlAttributes(new { style = "text-align:center;" }).Width(135);
})
.ToolBar(tool => tool.Template(
#"<a id='lnkexportExcel' class='k-button k-button-icontext ' href='/Report/Tasks'><span></span>ExportToExcel</a>))
.Pageable()
.AutoBind(false)
.Sortable()
.Scrollable(s => s.Enabled(true))
.ClientDetailTemplateId("template")
)
</div>
<div class="col-md-12">
<script id="template" type="text/kendo-tmpl">
#(Html.Kendo().Grid<ProjectName.Models.ViewModel.MyViewModel>()
.Name("grid_#=Id#")
.Columns(columns =>
{
columns.Bound(c => c.SubColumn1).HtmlAttributes(new { style = "text-align:center; overflow: hidden; white-space: nowrap;" }).HeaderHtmlAttributes(new { style = "text-align:center;" }).Width(100);
columns.Bound(c => c.SubColumn2).HtmlAttributes(new { style = "text-align:center; overflow: hidden; white-space: nowrap;" }).HeaderHtmlAttributes(new { style = "text-align:center;" }).Width(100);
})
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("ReadActionName", "ControllerName",
new { AgentSelectedId = "#=AgentId#", TaskHeaderSelectedId = "#=TaskHeaderId#"})
.Data("additionalData2"))
)
.Pageable()
.Sortable()
.ToClientTemplate()
)
</script>
</div>
And the two additional data which js javascript code :
function additionalData() {
return someModel;}
function additionalData2() {
return someModel;}
If it was useful, check it as an answer, please

Styling Html.ValidationMessageFor with Bootstrap3

I'm trying to style my #HTML.ValidationMessageFor element with Bootstrap 3 but no CSS is applied. When consulting Bootstrap's documentation, it states:
To use, add .has-warning, .has-error, or .has-success to the parent element. Any .control-label, .form-control, and .help-block within that element will receive the validation styles.
Even after nesting the desired #Html.ValidationMessageFor as stated above, no styling is applied:
Please see my code snippets below:
Model Property:
[Range(0, int.MaxValue, ErrorMessage = "Client ID cannot be larger than 2147483647.")]
[RegularExpression(#"^[0-9]+$", ErrorMessage = "Client ID must be a positive number.")]
public int searchClientID { get; set; }
View:
<div class="form-group col-sm-4">
<div class="input-group">
<span class="input-group-addon" id="client-ID">Client ID</span>
#Html.TextBoxFor(m => m.searchClientID, new { #class = "form-control" })
</div>
<div class="has-error">
#Html.ValidationMessageFor(m => m.searchClientID, null, new { #class = "has-error" })
</div>
</div>
EDIT WITH ANSWER:
<div class="has-error">
#Html.ValidationMessageFor(m => m.searchClientName, String.Empty, new { #class = "help-block" })
</div>
dont replace form-group with has-error.I have used something similar to this recently and try the following markup.
<div class="form-group has-error">
<div >
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "control-label" })
#Html.ValidationMessageFor(m => m.ConfirmPassword, "", new { #class = "help-block" })
</div>

Kendo DateTimePicker set value from json result

I have a big trouble with pasting a value in Kendo DateTimePicker inside Kendo Grid from json result. So I have two forms on page. In first I'm loading file:
#using (Html.BeginForm("GetImportSalesModelFromFile", "ExportImport", FormMethod.Post, new { id = "GetImportSaleModelFromFileForm", enctype = "multipart/form-data" }))
{
<input id="importFileInput" type="file" accept="text/csv" name="file" class="user-success" required>
<input style="width: 100px;" type="submit" value="Add">
}
On form submit called function
$('#GetImportSaleModelFromFileForm').submit(function(e) {
e.preventDefault();
var url = $(this).attr('action');
var xhr = new XMLHttpRequest();
var fd = new FormData();
fd.append("file", document.getElementById('importFileInput').files[0]);
xhr.open("POST", url, true);
xhr.send(fd);
xhr.addEventListener("load", function(event) {
AppendImportModel(JSON.parse(event.target.response));
}, false);
});
In controller I get needed import model
public ActionResult GetImportSalesModelFromFile(HttpPostedFileBase file)
{
var importModel = _importService.ConstructSaleImportModel(file.InputStream, file.ContentType);
return Json(importModel, JsonRequestBehavior.AllowGet);
}
In function AppendImportModel I parse result and paste it in kendo grid in second form
#(Html.Kendo().Grid<ImportSaleItemModel>().Name("ImportSalesGrid")
.DataSource(dataSource => dataSource.Ajax())
.Events(x => x.DataBound("initMenus"))
.Columns(columns =>
{
columns.Bound(x => x.Goods.PictureId)
.ClientTemplate("<img style='height: 50px;' src='" + Url.Action("Contents", "FileStorage", new { id = "#= Goods.PictureId #" }) + "'/>")
.Title("")
.Sortable(false)
.HtmlAttributes(new Dictionary<string, object> { { "style", "padding: 3px !important; height: 52px !important; width:52px !important;" } });
columns.Bound(x => x.Goods.Title)
.ClientTemplate("<a onclick='ShowInfoGoodWindow(#= Goods.Id #)'>#= Goods.Title #</a><br>" +
"<span><b>#= Goods.Article #</b> <descr>#= Goods.Description #</descr></span><br><input type='hidden' name='ImportedGoodList[#= index(data)#].Id' value='#= Goods.Id #' />")
.Title("Description");
columns.Bound(x => x.Price)
.ClientTemplate("<input class='priceEditor' maxlength='10' style='width:50px; text-align: center;' type='text' name='ImportedGoodList[#= index(data)#].Price' onkeypress='return isPriceKey(event)' oninput='$(this).get(0).setCustomValidity(clearValidation);' value='#=Price.ParsedValue #'>")
.HtmlAttributes(new Dictionary<string, object> { { "style", "text-align: center;" } })
.Title("Price");
columns.Bound(x => x.Discount)
.ClientTemplate("<input class='discountEditor' maxlength='10' style='width:50px; text-align: center;' type='text' name='ImportedGoodList[#= index(data)#].Discount' onkeypress='return isPriceKey(event)' oninput='$(this).get(0).setCustomValidity(clearValidation);' value='#=Discount.ParsedValue #'>")
.HtmlAttributes(new Dictionary<string, object> { { "style", "text-align: center;" } })
.Title("Discount");
columns.Bound(x => x.DepartmentId)
.HtmlAttributes(new { #class = "templateCell" })
.ClientTemplate(Html.Kendo().DropDownList().Name("Department#=LineId#").BindTo(Model.Departments).Value("#= DepartmentId #").ToClientTemplate().ToHtmlString())
.Title("Department");
columns.Bound(x => x.SaleDateTime)
.HtmlAttributes(new { #class = "templateCell" })
.ClientTemplate(Html.Kendo().DateTimePicker().Name("SaleDateTime#=LineId#").Value("#= ConvertedSaleDateTime #").ToClientTemplate().ToHtmlString())
.Title("Sale Date");
columns.Bound(x => x.SellerId)
.HtmlAttributes(new { #class = "templateCell" })
.ClientTemplate(Html.Kendo().DropDownList().Name("Seller#=LineId#").BindTo(Model.Sellers).Value("#= SellerId #").ToClientTemplate().ToHtmlString())
.Title("Seller");
columns.Bound(x => x.IsCashPayment)
.ClientTemplate("<input type='checkbox' id='IsCashPayment#=LineId#' checked='#= IsCashPayment.ParsedValue #' class='regular-checkbox'/><label for='IsCashPayment#=LineId#'></label> Yes")
.Title("Is Cash Payment");
})
)
In all columns using "#= value #" works fine but not in this line
.ClientTemplate(Html.Kendo().DateTimePicker().Name("SaleDateTime#=LineId#").Value("#= ConvertedSaleDateTime #").ToClientTemplate().ToHtmlString())
"#= ConvertedSaleDateTime #" not changed on real value, but if I write
.ClientTemplate("#= ConvertedSaleDateTime #")
I will get right value "10/07/2013 13:15". And if I write
.ClientTemplate(Html.Kendo().DateTimePicker().Name("SaleDateTime#=LineId#").Value("10/07/2013 13:15").ToClientTemplate().ToHtmlString())
I will get Kendo DateTimePicker inside grid with value "10/07/2013 13:15"
How I can set value to this DateTimePicker from ConvertedSaleDateTime?
Please, help me. Thanks in advance.
I solved my problem via jQuery. Maybe someone needs this solution or knows something more beautiful.
In client template of SaleDateTime columnt I wrote:
columns.Bound(x => x.SaleDateTime).ClientTemplate("<input class='saleDateTimeEditor' id='SaleDateTime#=index(data)#' name='ImportedSalesList[#=index(data)#].SaleDateTime' value='#= ConvertedSaleDateTime #'>")
And in DataBound event of my kendo grid I initialized all kendo DateTimePickers:
$('.saleDateTimeEditor').each(function () {
var id = $(this).attr('id');
var value = new Date(Date.parse($(this).val()));
$("#" + id).kendoDateTimePicker({
value: value,
max: new Date(Date.now())
});
$('#'+id).attr('readonly', 'readonly');
});
ConvertedSaleDateTime is in format "yyyy/MM/dd hh:mm:ss"