I'm using MVC5 and I want to search for a result and stay at the same page, here is my method in my controller (LiaisonsProjetsPPController) that do the operation of searching :
public ActionResult IndexAjoutML(int id, string SearchString)
{
PartiesPrenantesEntities db = new PartiesPrenantesEntities();
ViewBag.idProjet = id;
ViewBag.searchString = SearchString;
IQueryable<ActivitesPP> qry = this.db.ActivitesPP.Intersect(from item in this.db.LiaisonsProjetsPP where item.idProjet == id select item.ActivitesPP).Include(model => model.Activites.CatActivites);
var act = from s in db.CatActivites
select s;
if (!String.IsNullOrEmpty(SearchString))
return PartialView("~/Views/ActivitesPP/IndexAjoutProjet.cshtml", this.db.ActivitesPP.Where(s => s.PartiesPrenantes.nomPP.Contains(SearchString)).Except(qry));
else
return PartialView("~/Views/ActivitesPP/IndexAjoutProjet.cshtml", this.db.ActivitesPP.Except(qry));
}
Then in my view (Views/ActivitesPP/IndexAjoutProjet) I have my search form and the div of the display result :
#using (Ajax.BeginForm("IndexAjoutML", "LiaisonsProjetsPP", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "search-results"
}, new { #id = "searchFormPP" }))
{
<p>
<label>Partie prenante: </label> #Html.TextBox("SearchString")
<input id="inputRecherche" name="SearchString" type="submit" value="Rechercher" />
</p>
}
<div id="search-results">
#{foreach (var catactivite in Model.GroupBy(model => model.Activites.CatActivites))
{
String couleurCategorie = catactivite.Key.couleurCategorie;
String couleurTexte = CustomHelpers.GetForegroundColor(couleurCategorie);
//Image de la partie prenante
<div class="panel-heading unhide" style="background-image: none; color: #couleurTexte; background-color: #couleurCategorie; padding: 2px;">
</div>
foreach (var pp in catactivite)
{
String nomPP = (pp.idPP == null ? "Inconnu" : pp.PartiesPrenantes.nomPP);
String dateAffichee;
String imgPP = "../../Images/Profils/" + (pp.PartiesPrenantes.imgPP ?? "avatar.png");
if (pp.finActivite == null)
{
dateAffichee = "Depuis le " + String.Format("{0:d/MM/yyyy}", pp.debutActivite);
}
else
{
dateAffichee = "Depuis le " + String.Format("{0:d/MM/yyyy}", pp.debutActivite) + ", jusqu'au " + String.Format("{0:d/MM/yyyy}", pp.finActivite);
}
<div class="panel panel-primary">
<div class="panel-heading unhide" style="color: #couleurTexte; background-color: #couleurCategorie;">
<div style="float: left">
<img class="imgPP img-circle" src="#(imgPP)" />
</div>
<h5>#pp.Activites.libelleActivite (#Html.Raw(pp.idLieu == 999 ? "National" : pp.Lieux.nomLieu))</h5>
<h6>#pp.PartiesPrenantes.nomPP</h6>
</div>
<div class="panel-body hiddenPart">
#if (pp.idPP != null)
{
<label>Commentaire</label>
<p>#(pp.commentaireActivite ?? "Pas plus de détails..")</p>
#Html.Action("CreateForm", "LiaisonsProjetsPP", new { idActivite = pp.idActivite, idProjet = ViewBag.idProjet })
}
</div>
</div>
}
}
}
</div>
}
else
{
#Html.Raw("<p>Aucune partie prenante disponible..")
#Html.Raw("(attention: pour être ajoutée, une partie prenante doit posséder au moins une activité référencée..)</p>")
}
In my view i call my method of search (Views/Projets/Details): #{ Html.RenderAction("IndexAjoutML", "LiaisonsProjetsPP", new { idProjet = Model.idProjet, searchString = Model.searchString }); }
The search work but it redirects me to another page http://localhost:49612/LiaisonsProjetsPP/IndexAjout/1 instead of staying at this page http://localhost:49612/Projets/Details/1.
What you are trying to do seems to be loading results unobtrusively with an AJAX form. The Professional ASP.NET MVC 5 book by John Galloway et al. has a nice section on this, but realizing that no one reads books I will provide a link to a site with a code sample for it.
The .NET Funda site describes here exactly how to search and return the results to the same page without a complete refresh using unobtrusive-ajax.
Likely what you are missing is a reference to jquery.unobtrusive-ajax.min.js. Other posts on Stack Overflow also reference this topic, but I realize you might not know the correct search terms. Try looking further into "AJAX partial view unobtrusive loading" as search terms for further research.
This example is from the book I mentioned from John Galloway.
A JavaScript error message.
function searchFailed(){
$("#searchresults").html("Sorry, there was a problem searching.");
}
This is what a simple Ajax form should look like. Note the "GET" form method.
<div class="panel panel-default">
<div class="panel-heading">
Artist Search
</div>
<div class="panel-body">
#using(Ajax.BeginForm("ArtistSearch", "Home",
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
OnFailure = "searchFailed",
LoadingElementID = "ajax-loader",
UpdateTargetId = "searchresults",
}))
{
<input type="text" name="q" />
<input type="submit" value="search" />
<img id="ajax-loader"
src="#Url.Content("~/Images/ajax-loader.gif")"
style="display:none" />
}
<div id="searchresults"></div>
</div>
</div>
This is the method responsible for returning the partial view:
public ActionResult ArtistSearch(string q)
{
var artists = GetArtists(q);
return PartialView(artists);
}
This is a search method.
public List<Artist> GetArtists(string searchString)
{
return storeDB.Artist.Where(a => a.Name.Contains(searchString)).ToList();
}
Note that the method for returning the partial view is simply "return PartialView(model);"
Related
I have a page in my React App that renders some locally stored JSON Data into a table. I've been trying for a long time to implement a search bar that will filter through the PostCode column in the table and return the row. So far I've been able to make a search bar that does what I want it to but I haven't been able to integrate it into the page with the table.
My apologies if this is an easy question or I'm way off the mark. I'm a coding novice but have to use React for a project and struggle to get to grips with it.
The code for my table looks like this:
import SearchBar from './SearchBar'
import React,{useState,useEffect} from 'react';
import './App.css';
import { Data } from './NewData.js'
export const JsonReader = () => {
return (
<>
<ArchiveHeader />
<div className="data-container">Welcome to RF</div>
{Data.map((data, key) => {
return (
<div key={key}>
<JsonTable
key = {key}
Username = {data.Username}
Address = {data.Address}
PostCode = {data.PostCode}
Details ={data.Details}
Date ={data.Date}
Score ={data.Score}
/>
</div>
);
})}
</>
);
};
const ArchiveHeader = () => {
return (
<header className="ArchiveHeader">
<h2>Rent Flag</h2>
</header>
);
};
const JsonTable= ({ Username, Address, PostCode, Details, Date, Score }) => {
if (!Username) return <div />;
return (
<table data={Data}>
<tbody>
<tr>
<td>
<h5>{Username}</h5>
</td>
<td>
<h5>{Address}</h5>
</td>
<td>
<h4>{PostCode}</h4>
</td>
<td>
<p>{Details}</p>
</td>
<td>
<p>{Date}</p>
</td>
<td>
<p>{Score}</p>
</td>
</tr>
</tbody>
</table>
);
};
export default JsonReader;
and the code for my searchbar looks like this:
import Papa from "papaparse";
import React, { useState, useEffect } from 'react';
import { Data } from './NewData.js'
import JsonReader from './JsonReader'
export default function SearchBar () {
const [searchTerm,setSearchTerm] = useState('')
return (
<div className="SearchBar">
<input type="text" placeholder="search..." onChange={e=>setSearchTerm(e.target.value)} />
{Data.filter((val)=>{
if(searchTerm == ""){
return val
}
else if(val.PostCode.toLowerCase().includes(searchTerm.toLowerCase())){
return val;
}
}).map((val,key)=>{
return <div>{val.PostCode} </div>
})}
</div>
);
}
You can use useMemo hook for this functionality.
const searchResults = useMemo(() => {
if (!searchTerm) {
return items;
}
return Data?.filter((item) =>
item?.toLowerCase()?.includes(searchTerm?.toLowerCase().trim())
);
}, [Data, searchTerm]);
You can use searchResults variable and render that in your table.
In the same JsonReader function you should:
Create the useState constants [searchTerm, setSearchTerm].
Set a new FData array with filtered data and that is the one you should do the mapping to.
Place the input field on the same page.
And, instead of using the Data.map you will use the FData.map.
This is how your JsonReader function should look like (and the SearchBar function could be discarded):
export const JsonReader = () => {
const [searchTerm, setSearchTerm] = useState("");
const FData = Data.filter((val) => {
if (searchTerm == "") {
return val;
} else if (val.PostCode.toLowerCase().includes(searchTerm.toLowerCase())) {
return val;
}
});
return (
<>
<ArchiveHeader />
<div className="data-container">Welcome to RF</div>
<input type="text" placeholder="search..." onChange={(e) => setSearchTerm(e.target.value)} />
{FData.map((data, key) => {
return (
<div key={key}>
<JsonTable
key = {key}
Username = {data.Username}
Address = {data.Address}
PostCode = {data.PostCode}
Details ={data.Details}
Date ={data.Date}
Score ={data.Score}
/>
</div>
);
})}
</>
);
};
I have two classes:
Row (Child Class)
mySpreadsheet (Parent Class)
I am trying to do something like this:
Row:
class Row extends React.Component {
constructor(props, ref) {
super(props);
this.state = { selectedFile: null}
this.handleUpload = this.handleUpload.bind(this);
}
//This handleUpload is being called by the parent class of this Row class through ref.
handleUpload(ev) {
ev.preventDefault();
const data = new FormData();
data.append('file', this.uploadInput.files[0]);
data.append('filename', this.fileName.value);
data.append('comment',this.comment.value);
data.append('id', this.fileName.id);
fetch('http://localhost:8000/upload', {
method: 'POST',
body: data,
}).then((response) => {
response.json().then((body) => {
this.setState({ selectedFile: `http://localhost:8000/${body.file}` });
});
});
}
rowCreator() {
let row = []
for (var i = 0; i < 10; i++) {
row.push(
<td>
<div>
<input type="file" name={`file${this.props.id*10 + i}`} id={this.props.id*10 + 1} ref={(ref) => { this.uploadInput = ref; }}/>
<input type="text" name={`fileName ${this.props.id*10 + i}`} ref={(ref) => { this.fileName = ref; }} placeholder="Name the file with extension"/>
<input type="text" ref={(ref) => { this.comment = ref; }} placeholder="Comment"/>
</div>
</td>
)
}
return row
}
render() {
return (
<tr>
<td class="align-middle ">
<div class="cell">
<input type="text" placeholder={this.props.id + 1} />
</div>
</td>
{this.rowCreator()}
</tr>
)
}
}
and in mySpreadsheet I am creating each row in a table using Row class as follows:
<tbody id="tbody">
{this.state.data.map(id => (
<Row id={id} ref={this.rowRef} />
))}
</tbody>
I am using the handleUpload() function from the Row (child) by using rowRef:
this.rowRef = React.createRef();
upload(ev) {
this.rowRef.current.handleUpload(ev);
}
<button onClick={this.upload}>
Upload Files
</button>
But I am getting error (500) while doing a POST request through my website. Is it because of the ref I am using in Row such as uploadInput, for appending data in handleUpload? Is there any way to make a unique ref for all the cells in my table? Or can I use something else like id or name which I have made uniquely for all different cells using this.props.id*10 + i for each iteration, i while making columns for one row?
You can create refs for mapped elements and put it in an array based on their indexes or ids.
// constructor state part
constructor() {
this.refs = [];
}
// render part
{this.state.data.map(id => (
return <Row id={id} ref={itemRef => this.refs[id] = itemRef} />
))}
And in my opinion, you should hold the values instead of the components itself in an array.
Id must be unique and if the map parameter is an object, you should use a unique property instead of the key which comes with map. If you use keys, React will not keep track of your component while you' re updating your state.
Using the below SPFx PnP.js code I am doing the CRUD operation in SharePoint online list:
private _getListData(): Promise<ISPList[]> {
return pnp.sp.web.lists.getByTitle("EmployeeList").items.get().then((response) => {
return response;
});
}
private getListData(): void {
this._getListData()
.then((response) => {
this._renderList(response);
});
}
AddItem()
{
pnp.sp.web.lists.getByTitle('EmployeeList').items.add({
EmployeeName : document.getElementById('EmployeeName')["value"],
Experience : document.getElementById('Experience')["value"],
Location:document.getElementById('Location')["value"]
});
alert("Record with Employee Name : "+ document.getElementById('EmployeeName')["value"] + " Added !");
}
UpdateItem()
{
var id = document.getElementById('EmployeeId')["value"];
pnp.sp.web.lists.getByTitle("EmployeeList").items.getById(id).update({
EmployeeName : document.getElementById('EmployeeName')["value"],
Experience : document.getElementById('Experience')["value"],
Location:document.getElementById('Location')["value"]
});
alert("Record with Employee Name : "+ document.getElementById('EmployeeName')["value"] + " Updated !");
}
DeleteItem()
{
pnp.sp.web.lists.getByTitle("EmployeeList").items.getById(document.getElementById('EmployeeId')["value"]).delete();
alert("Record with Employee ID : "+ document.getElementById('EmployeeId')["value"] + " Deleted !");
}
In the above code AddItem() and UpdateItem() are not working but reading list items and deleting list item are working. I mean I am not able to add new record to the list and update also not getting any errors, getting the successful alert message but it is not adding or updating the items. Any issue with the AddItem() and updateItem() function code....any help will be much appreciated.
Updated
I am not getting any error though it add and update function is not working, by the way I am global administrator.
While clicking on the add button getting the successful alert message but it is not adding, screenshot attached:
Complete code reference:
import pnp from 'sp-pnp-js';
//import { default as pnp, ItemAddResult } from "sp-pnp-js";
import { Version } from '#microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '#microsoft/sp-webpart-base';
import { escape } from '#microsoft/sp-lodash-subset';
import styles from './PnPspCrud.module.scss';
import * as strings from 'pnPspCrudStrings';
import { IPnPspCrudWebPartProps } from './IPnPspCrudWebPartProps';
export interface ISPList {
ID: string;
EmployeeName: string;
Experience: string;
Location: string;
}
export default class PnPspCrudWebPart extends BaseClientSideWebPart<IPnPspCrudWebPartProps> {
private AddEventListeners() : void{
document.getElementById('AddItem').addEventListener('click',()=>this.AddItem());
document.getElementById('UpdateItem').addEventListener('click',()=>this.UpdateItem());
document.getElementById('DeleteItem').addEventListener('click',()=>this.DeleteItem());
}
private _getListData(): Promise<ISPList[]> {
return pnp.sp.web.lists.getByTitle("EmployeeList").items.get().then((response) => {
return response;
});
}
private getListData(): void {
this._getListData()
.then((response) => {
this._renderList(response);
});
}
private _renderList(items: ISPList[]): void {
let html: string = '<table class="TFtable" border=1 width=100% style="border-collapse: collapse;">';
html += `<th>EmployeeId</th><th>EmployeeName</th><th>Experience</th><th>Location</th>`;
items.forEach((item: ISPList) => {
html += `
<tr>
<td>${item.ID}</td>
<td>${item.EmployeeName}</td>
<td>${item.Experience}</td>
<td>${item.Location}</td>
</tr>
`;
});
html += `</table>`;
const listContainer: Element = this.domElement.querySelector('#spGetListItems');
listContainer.innerHTML = html;
}
public render(): void {
this.domElement.innerHTML = `
<div class="parentContainer" style="background-color: lightgrey">
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
<span class="ms-font-xl ms-fontColor-white" style="font-size:28px">Welcome to SharePoint Framework Development using PnP JS Library</span>
<p class="ms-font-l ms-fontColor-white" style="text-align: left">Demo : SharePoint List CRUD using PnP JS and SPFx</p>
</div>
</div>
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}">
<div style="background-color:Black;color:white;text-align: center;font-weight: bold;font-size:18px;">Employee Details</div>
</div>
<div style="background-color: lightgrey" >
<form >
<br>
<div data-role="header">
<h3>Add SharePoint List Items</h3>
</div>
<div data-role="main" class="ui-content">
<div >
<input id="EmployeeName" placeholder="EmployeeName" />
<input id="Experience" placeholder="Experience" />
<input id="Location" placeholder="Location" />
</div>
<div></br></div>
<div >
<button id="AddItem" type="submit" >Add</button>
</div>
</div>
<div data-role="header">
<h3>Update/Delete SharePoint List Items</h3>
</div>
<div data-role="main" class="ui-content">
<div >
<input id="EmployeeId" placeholder="EmployeeId" />
</div>
<div></br></div>
<div >
<button id="UpdateItem" type="submit" >Update</button>
<button id="DeleteItem" type="submit" >Delete</button>
</div>
</div>
</form>
</div>
<br>
<div style="background-color: lightgrey" id="spGetListItems" />
</div>
`;
this.getListData();
this.AddEventListeners();
}
AddItem()
{
pnp.sp.web.lists.getByTitle('EmployeeList').items.add({
EmployeeName : document.getElementById('EmployeeName')["value"],
Experience : document.getElementById('Experience')["value"],
Location:document.getElementById('Location')["value"]
});
alert("Record with Employee Name : "+ document.getElementById('EmployeeName')["value"] + " Added !");
}
/*
AddItem()
{
pnp.sp.web.lists.getByTitle('EmployeeList').items.add({
EmployeeName : document.getElementById('EmployeeName')["value"],
Experience : document.getElementById('Experience')["value"],
Location:document.getElementById('Location')["value"]
})
.then(addResponse => {
alert("Record with Employee Name : "+ document.getElementById('EmployeeName')["value"] + " Added !");
})
.catch(addError => alert("An error has occurred:" + JSON.stringify(addError)));
}
*/
/*
private AddItem():void
{
pnp.sp.web.lists.getByTitle("EmployeeList").items.add({
EmployeeName : document.getElementById('EmployeeName')["value"],
Experience : document.getElementById('Experience')["value"],
Location: document.getElementById('Location')["value"]
})
.then((iar: ItemAddResult) => {
console.log(iar);
})
.catch((error:any) => {
console.log("Error: ", error);
});
}
*/
UpdateItem()
{
var id = document.getElementById('EmployeeId')["value"];
pnp.sp.web.lists.getByTitle("EmployeeList").items.getById(id).update({
EmployeeName : document.getElementById('EmployeeName')["value"],
Experience : document.getElementById('Experience')["value"],
Location:document.getElementById('Location')["value"]
});
alert("Record with Employee Name : "+ document.getElementById('EmployeeName')["value"] + " Updated !");
}
DeleteItem()
{
pnp.sp.web.lists.getByTitle("EmployeeList").items.getById(document.getElementById('EmployeeId')["value"]).delete();
alert("Record with Employee ID : "+ document.getElementById('EmployeeId')["value"] + " Deleted !");
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}
Note:
I am referring this link where complete code is available.
https://www.sharepoint-journey.com/SharePoint-Framework-Development-Create-List-CRUD-WebPart-PnPJS.html
Of course you get the alert function to call, because the add/update/delete functions from pnp/sp are asynchronous! you can try that:
pnp.sp.web.lists.getByTitle('EmployeeList').items.add({
EmployeeName : document.getElementById('EmployeeName')["value"],
Experience : document.getElementById('Experience')["value"],
Location:document.getElementById('Location')["value"]
})
.then(addResponse => {
alert("Record with Employee Name : "+ document.getElementById('EmployeeName')["value"] + " Added !");
})
.catch(addError => alert("An error has occurred:" + JSON.stringify(addError)));
I hope this helps, maybe if you get an error here you can update your post to dig deeper into the problem!
Greetings
code has no issue. Best practice should be as mentioend #j.Benda. It seems permission issue.
open in chorme network console (f12 and go to network). it may give u the error which is coming exactly. site url is not set kind. I will try ur code and let u know. try below.
import { default as pnp, ItemAddResult, Web } from "sp-pnp-js";
on button events set web url.
onclickevnet(){
var NewISiteUrl = this.props.siteurl;
var NewSiteUrl = NewISiteUrl.replace("/SitePages", "");
console.log(NewSiteUrl);
let webx = new Web(NewSiteUrl)
webx.lists.getByTitle('EmployeeList').items.add({
EmployeeName : document.getElementById('EmployeeName')["value"],
Experience : document.getElementById('Experience')["value"],
Location:document.getElementById('Location')["value"]
})
.then(addResponse => {
//error
})
.catch(addError => alert("An error has occurred:" + JSON.stringify(addError)));
}
I encountered problem with react component. Basically, i use NPM integrated with Rails and called React component in my rails app.
I try to call react-file-base64 module in my code like below :
const FileBase64 = require('react-file-base64');
var MembersNew = React.createClass(
{
render()
{
return(
<div>
<h5>ACCOUNT DETAILS</h5>
<hr/>
<p>Fill in your member account details below</p>
<b>Membership ID : </b>
<div className="row">
<div className="medium-6 columns">
<FileBase64/> <------ Called component
<label>Username*
<input ref="name" type="text"/>
</label>
<label>First Name*
<input ref="name" type="text"/>
</label>
<label>Last Name*
<input ref="name" type="text"/>
</label>
<label>Email Address*
<input ref="name" type="text"/>
</label>
</div>
</div>
)
I got following error :
Below is react-file-base64.js :
import React from 'react';
import ReactDOM from 'react-dom';
class FileBase64 extends React.Component {
constructor() {
super()
this.state = {
files: []
}
this.props = {
multiple: false
}
}
handleChange(e){
// get the files
let files = e.target.files;
// Process each file
var allFiles = []
for (var i = 0; i < files.length; i++) {
let file = files[i]
// Make new FileReader
let reader = new FileReader()
// Convert the file to base64 text
reader.readAsDataURL(file)
// on reader load somthing...
reader.onload = () => {
// Make a fileInfo Object
let fileInfo = {
name: file.name,
type: file.type,
size: Math.round(file.size / 1000)+' kB',
base64: reader.result,
file: file
}
// Push it to the state
allFiles.push(fileInfo)
// If all files have been proceed
if(allFiles.length == files.length){
// Apply Callback function
if(this.props.multiple) this.props.onDone(allFiles)
else this.props.onDone(allFiles[0])
}
} // reader.onload
} // for
}
render(){
return (
<div>
<input
type="file"
onChange={ this.handleChange.bind(this) }
multiple={ this.props.multiple } />
</div>
)
}
}
export default FileBase64;
I think what i did was wrong. If there's anyone could guide me on how can i achieve this. I really appreciate that.
Domain
class Example {
String title
Date releaseDate
String author
Boolean paperback
static constraints = {
}
}
Controller
class ExampleController {
def scaffold = true
def search = {
exampleInstance = Example.findAllByTitleIlike("${params.q}%",[max:10, offset:0, sort:"title", order:"asc"])
redirect(action: "list",params)
return
}
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[exampleInstanceList: Example.list(params),
exampleInstanceTotal: Example.count()]
}
}
View
<div id="search">
<g:form url='[controller: "example", action: "search"]' id="search" name="search" method="get">
<g:textField name="q" value="${params.q}" />
<input type="submit" value="Find" />
</g:form>
</div>
Please Help me is how
Error URI /test3/example/search Class groovy.lang.MissingPropertyException
Message No such property: exampleInstance for class: test3.ExampleController
Use render instead of redirect and pass all the model that are required for the list page.
def search = {
def exampleInstance= Example.findAllByTitleIlike("${params.q}%",[max:10, offset:0, sort:"title", order:"asc"])
render( view:list, model:[ exampleInstance:exampleInstance, params:params ] )