I have a list that must be filled with a bit of code behind (prepopulating some fields, and do a bit of work on the save button.)
What is the best way to do that ?
thx
Edit: I ended by creating a custom webpart on the default.aspx. In this web part I have a bunch of :
<table border="0" cellspacing="0" width="100%">
<tr>
<td width="190px" valign="top" class="ms-formlabel">
<h3 class="ms-standardheader">
<nobr>Title<span class="ms-formvalidation"> *</span>
</nobr>
</h3>
</td>
<td width="400px" valign="top" class="ms-formbody">
<SharePoint:FormField runat="server" id="fldTitle" ControlMode="New" FieldName="Title" ListId="{MyListID}" />
<SharePoint:FieldDescription runat="server" id="ff1description" FieldName="Title" ControlMode="New" ListId="{MyListID}"/>
</td>
</tr>
This is working, but I found this in a bit painfull because I have to read each form field in code behind :
private void Set(SPListItem item, string fieldInternalName, object fieldValue)
{
var field = item.Fields.GetFieldByInternalName(fieldInternalName);
item[fieldInternalName] = fieldValue;
}
protected void Btn_Ok_Click(object sender, EventArgs e)
{
SPWeb thisWeb = SPContext.Current.Web;
SPList myList= thisWeb.Lists["mylist"];
SPListItem newItem;
newItem= myList.Items.Add();
var router = thisWeb.EnsureUser(#"myuser");
Set(newItem, "Title", fldTitle.Value);
Set(newItem, "OtherField", fldOther.Value);
Set(newItem, "AnotherField", GetFromBusinessLogic());
SPUtility.Redirect(thisWeb.Url, SPRedirectFlags.Default, System.Web.HttpContext.Current);
newItem.Update();
}
Is there any way to wrap all of this in a custom form container ? Maybe a custom ListFormWebPart with inner templates and code-behind events ?
You can follow the suggestion here to add in the code-behind.
BUT even if you put in a code-behind file, you still cannot reference the controls (fields, save buttons etc) on the form from the code-behind.
I would suggest writing up a web service (asmx or WCF) and host it on the Sharepoint instance.
You can then use javascript and AJAX calls to perform initialization and validation.
Related
I want to be able to capture keyboard input without using an HTML INPUT tag in Blazor. Once the key is pressed i will display a graphic to represent the letter pressed.
Something like this
#page "/counter"
#using Microsoft.AspNetCore.Components.Web
<div #onkeypress="e => KeyPress(e)">
Press any letter key
</div>
#code {
private void KeyPress(KeyboardEventArgs e)
{
var letter = e.Key;
}
}
The KeyPress method does not appear to be called when I set a breakpoint on it. Any help much appreciated.
If there is still someone who want the solution. I think now in .NET 5 you can achieve this in Blazor without js. To set focus and tabindex is important, when you lost focus, or set focus to another element this will not work. This works for me:
<table #ref="testRef" tabindex="0" #onkeydown="HandleKeyDown">
<thead>
<tr>
<th>
Pressed Key
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
#pressedKey
</td>
</tr>
</tbody>
</table>
private ElementReference testRef;
private string pressedKey;
private void HandleKeyDown(KeyboardEventArgs e)
{
pressedKey = e.Key;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await testRef.FocusAsync();
}
}
You are almost there, but you forgot to make div focused. This is the steps:
0.- Make your div focusable adding tabindex tag:
<div
class="jumbotron"
#onkeydown="#KeyDown"
tabindex="0"
#ref="myDiv" >
<h1 class="display-4">
#letter
</h1>
</div>
1.- Create a js code to set focus to your div, on _Host.cshtml for example:
<script>
window.SetFocusToElement = (element) => {
element.focus();
};
</script>
This function takes an Element reference as the parameter.
2.- Call this function after your component is rendered.
protected ElementReference myDiv; // set by the #ref attribute
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("SetFocusToElement", myDiv);
}
}
3.- Implement your own KeyDown:
protected void KeyDown(KeyboardEventArgs e)
{
letter = $"Pressed: [{e.Key}]";
}
Notice that this is not a Blazor issue, is just the default html and js behavior. I learned it writing a game, check it out at Blagario lab.
Running:
Demo at Flappy Blazor Bird
Edited Nov 2019:
Code improved by #Quango (many thanks)
Here is an easier way to do it (without JS).
First create a div like this:
<div #ref="keyDownDiv" #onkeydown="KeyPress"></div>
Add a Variable for your reference to your code:
private ElementReference keyDownDiv;
Now you can use this line:
await keyDownDiv.FocusAsync();
The Focus of the Browser will be set on your div and it will trigger the OnKeyDown event.
Tip: if the user presses any other button, the focus will change and it wont work anymore! In this case just use FocusAsync again.
I'm revamping a small helpdesk application (moving from ASP classic to MVC4). This is my first full MVC application. I've opted to go with Razor, and I feel like it's pretty intuitive. But I've hit a wall on this part.
I get a list of tickets from the database to display to the user. What I would like is to dynamically create a table. Right now I have put the headers in place manually, which is fine. If there's a way to do that dynamically, though, I'm open to suggestions.
But the crucial piece is to get each property from the ticket. I have a Ticket class that has over 20 properties. I'll be working on whittling those down to the minimum we want to display, but as a starting point, I'm trying to throw them all up on the screen.
I have the following:
#model IList<Helpdesk4.Models.Ticket>
...
#foreach (var ticket in Model)
{
<tr>
#foreach (var item in ticket)
{
<td>item</td>
}
</tr>
}
But I can't run that foreach on ticket. I'm enough of a noob that I don't totally understand why, but I think that the properties have to be loaded and so can't be enumerated by default. So without pulling up each property name, how do I just get each property's value from ticket?
I am using NHibernate for the queries to the db if that makes any difference.
The absolutely clearest and easiest way to do this if to manually add each of the properties values to the row. This does require a small amount of "extra" work, but it is a one time thing, and 20 properties is not that much. It also gives you much finer control over exactly how each property is displayed, if it is aligned right or left, etc.
What you end up with is something like this
#foreach (var ticket in Model)
{
<tr>
<td>
#ticket.FirstProperty
</td>
<td class="align-right">
#ticket.SecondProperty
</td>
<td class="align-center bold">
#Html.DisplayFor(m => ticket.ThirdProperty)
</td>
<td>
<i>#ticket.FourthProperty</i>
</td>
...
</tr>
}
Stylings and such added for emphasis of customizability.
Good luck with your first MVC project!
Foreach only works with an enumerable item, and general classes don't implement IEnumerable. You need to put each item on there yourself. The best way would be to use something like Html.TextBoxFor(m => m.Property) for each property.
edit:
The best way would be to just use Html.LabelFor on the object itself.
#for(int index = 0; index < Model.Count; ++index)
{
<tr>
#Html.LabelFor(m => m[index])
<tr>
}
I have a DefaultDataTable that gets its columns programmatically.
The markup is simple:
<table wicket:id="dataTable" border="0" cellpadding="1" cellspacing="1" width="90%" />
All of the columns are dynamically generated from a passed in LinkedHashMap of labels and attributes:
for (Entry<String, String> entry : entrySet) {
final String label = entry.getKey();
final String attribute = entry.getValue();
columns.add(new PsPropertyColumn(label, attribute) {
#Override
public void populateItem(Item cellItem, String componentId, IModel model)
{
final Object modelObject = model.getObject();
final Object value = PropertyResolver.getValue(attribute, modelObject);
// Add an edit link
BookmarkablePageLink link = new ...;
...
cellItem.add(link);
}
}
}
DefaultDataTable table = new DefaultDataTable("dataTable", columns, dataProvider, MAX_ROWS) {
...
}
add(table);
As many posts have mentioned, this is rendered as a cell with an onclick handler rather than an anchor (<a href="..." />) tag. I want the anchor tag for a couple of reasons, one if which is that I want to add my own onclick handler without having an existing onclick handler in the way.
I have seen the generally accepted solution that says to put an anchor tag inside a panel in the HTML markup, and to add the link inside of a Panel subclass. However, that doesn't give the entire html markup (inside the table), and I think the column () tags must be a part of this markup, which I don't think works with my strategy of dynamically generating the columns (I don't even know or care how many will be asked for). Is there a way to render my dynamically generated columns as anchor tags without specifying in the markup how many there?
Thank you for any help.
I have used nested ListView components to be able to have more control over the layout as below:
html:
<table wicket:id="listViewContainer">
<tr>
<th wicket:id="columnHeaderListView">
<span wicket:id="columnHeaderLabel">Header Label</span>
</th>
</tr>
<tr wicket:id="rowListView">
<td wicket:id="rowColumnListView">
<a href="#" wicket:id="link">
<span wicket:id="linkLabel">Link Label</span>
</a>
</td>
</tr>
</table>
Java:
WebMarkupContainer listViewContainer = new WebMarkupContainer("listViewContainer");
listViewContainer.setOutputMarkupId(true);
add(listViewContainer);
ListView columnHeaderListView = new ListView("columnHeaderListView", columnHeaderList) {
#Override
protected void populateItem(ListItem listItem) {
ColumnHeader ch = (ColumnHeader) listItem.getModelObject();
listItem.add(new Label("columnHeaderLabel", new Model(ch.getLabel())));
}
};
listViewContainer.add(columnHeaderListView);
ListView rowListView = new ListView("rowListView", rowList) {
#Override
protected void populateItem(ListItem listItem) {
Row row = (Row) listItem.getModelObject();
listItem.add(new ListView("rowColumnListView", getRowColumnList(row)){
#Override
protected void populateItem(ListItem li) {
RowColumn rowColumn = (RowColumn) li.getModelObject();
Link link = new BookmarkablePageLink("link",...
li.add(link);
link.add(new Label("linkLabel", new Model(rowColumn.getLabel())));
}
});
}
}
listViewContainer.add(rowListView);
I have a grid that populates from a search event and I'd like the option of being able to close the grid by simply adding an X in the top right corner, similar to how you close any browser or window. I thought it would be as easy as adding the X, styling it to my liking and then creating an onclick event that would close or hide the grid... but I can't seem to get that working. Any help would be appreciated.
My JS is:
dojo.require("dojox.grid.DataGrid"); //FindTask
dojo.require("dojo.data.ItemFileReadStore"); //FindTask
dojo.require("esri.tasks.find"); //FindTask
var findTask, findParams;
var grid, store;
var searchExtent;
function doFind() {
//Show datagrid onclick of search button and resize the map div.
esri.show(datagrid);
dojo.style(dojo.byId("content"), "height", "83%");
searchExtent = new esri.geometry.Extent ({
"xmin":-9196258.30121186,"ymin":3361222.57748752,"xmax":-9073959.055955742,"ymax":3442169.390441412,"spatialReference":{"wkid":102100}
});
map.setExtent(searchExtent);
//Set the search text to the value in the box
findParams.searchText = dojo.byId("parcel").value;
grid.showMessage("Loading..."); //Shows the Loading Message until search results are returned.
findTask.execute(findParams,showResults);
}
function showResults(results) {
//This function works with an array of FindResult that the task returns
map.graphics.clear();
var symbol = new esri.symbol.SimpleFillSymbol(esri.symbol.SimpleFillSymbol.STYLE_SOLID, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([98,194,204]), 2), new dojo.Color([98,194,204,0.5]));
//create array of attributes
var items = dojo.map(results,function(result){
var graphic = result.feature;
graphic.setSymbol(symbol);
map.graphics.add(graphic);
return result.feature.attributes;
});
//Create data object to be used in store
var data = {
identifier: "Parcel Identification Number", //This field needs to have unique values. USES THE ALIAS!!!
label: "PARCELID", //Name field for display. Not pertinent to a grid but may be used elsewhere.
items: items
};
//Create data store and bind to grid.
store = new dojo.data.ItemFileReadStore({ data:data });
var grid = dijit.byId('grid');
grid.setStore(store);
//Zoom back to the initial map extent
map.setExtent(searchExtent);
}
//Zoom to the parcel when the user clicks a row
function onRowClickHandler(evt){
var clickedTaxLotId = grid.getItem(evt.rowIndex).PARCELID;
var selectedTaxLot;
dojo.forEach(map.graphics.graphics,function(graphic){
if((graphic.attributes) && graphic.attributes.PARCELID === clickedTaxLotId){
selectedTaxLot = graphic;
return;
}
});
var taxLotExtent = selectedTaxLot.geometry.getExtent();
map.setExtent(taxLotExtent);
}
and my HTML is:
<div id ="datagrid" data-dojo-type="dijit.layout.AccordionPane" splitter="true" region="bottom"
style="width:100%; height:125px;">
<table data-dojo-type="dojox.grid.DataGrid" data-dojo-id="grid" id="grid" data-dojo-props="rowsPerPage:'5', rowSelector:'20px'">
<thead>
<tr>
<th field="Parcel Identification Number" width="10%">
Parcel ID
</th>
<th field="Assessing Neighbornood Code" width ="20%">
Neighborhood Code
</th>
<th field="Property Class Code" width="10%">
Property Class
</th>
<th field="Site Address" width="100%">
Address
</th>
</tr>
</thead>
</table>
</div>
This is my best guess at what to add:
<tr>
<td align="right">
<div class="divOk" onclick="dijit.byId('tocDiv').hide();">
OK</div>
</td>
</tr>
I wound up creating a work around for what I want by creating a new column and putting a close icon within the header. I connected it to an function so that when I click it, the grid closes and the map resizes:
function closeGrid() {
esri.hide(datagrid);
dojo.style("map", {"height": "100%"});
}
HTML
<th field="" width="2%"> <div class="GridCloseIcon" title="Close Grid" onclick="closeGrid();"></div>
How about this?
(assuming the OK line actually appears)
HTML
<tr>
<td align="right">
<div class="divOk" onclick="hideGrid();">OK</div>
</td>
</tr>
JS
function hideGrid() {
var widget = dijit.byId('datagrid');
dojo.fadeOut({
node: widget.domNode
}).play();
dojo.style(widget.domNode, 'display', 'none');
}
I have a problem using Struts1 ActionForm beans. Please, see a part of my struts-config:
<!-- RuleSearchForm is a sublass of RuleForm -->
form-beans>
<form-bean name="ruleForm"
type="forms.RuleForm">
</form-bean>
<form-bean name="ruleSearchForm"
type="forms.RuleSearchForm">
</form-bean>
</form-beans>
<!-- Mappings -->
<action path="/RuleList"
type="actions.RuleList"
name="ruleSearchForm"
scope="session"
validate="false">
<forward name="success" path="/html/view/RuleList.jsp"></forward>
</action>
<action path="/RuleCreate"
type="actions.RuleCreate"
name="ruleForm"
scope="request"
validate="false">
<forward name="success" path="/html/view/CreateUpdateRule.jsp"></forward>
</action>
And parts of my Actionform beans code:
public class RuleForm extends ActionForm {
protected Integer crid;
protected List levels;
/** Some other fileds go here */
public Collection getLevels(){
if(levels == null){
levels = DAOClass.getLevels();
Collections.reverse(levels);
}
return levels;
}
/** Other getters/setters go here */
}
public class RuleSearchForm extends RuleForm{
/**
* Avoid filter reset. If needs to be reset use {#link RuleForm#resetBeanFields()} directly.
* */
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
/**
* Add empty value. User should have an opportunity not to set value for this field.
* */
public Collection getLevels(){
if(levels == null || levels.size() == 0){
super.getLevels();
levels.add(0, new Level());
}
return levels;
}
}
The problem is:
User goes to /RuleList.do and sees the list of rules. ruleSearchForm used as a bean for transferring search params to /RulesList.do action. Initially tihs bean is empty, only getLevels() returns "empty value" + list of levels got from superclass method.
User goes to /CreateRule.do, ruleForm is used for collecting user input. levels property is used in selectbox. And I get there list of levels + empty row. This empty row is not added in RuleForm (named ruleForm). It's added it subclass of RuleForm. Why does super class ActionForm bean with NO static fields, with other name gets values from it's sublass instance???
If users saves Rule and has been redirected to /RuleList.do, the he sees populated (i.e. filled) search form ("ruleSearchForm") with values from "ruleForm".
What does it mean? Please help, I don't understand this mess of data beetween ActionForm Beans
UPD:
Now, I've changed inheritance of FormAction beans. I've introduced BaseFormBean. This BaseFormBean got two children: RuleForm and RuleSearchForm.
It didn't help. Still attributes from one bean are moved to another.
My jsp code:
CreateUpdateRule.jsp:
<html:form action="/RuleSave.do">
<html:hidden property="crid"/>
<table border="0" cellpadding="2" cellspacing="0">
<tr>
<td><bean:message key="rule.levelId"/></td>
<td><html:select property="levelId">
<html:optionsCollection property="levels" value="clid" label="name" />
</html:select>
</tr>
<tr>
<td><bean:message key="rule.timeStart"/></td>
<td><html:text property="timeStartStr"/></td>
</tr>
<tr>
<td><bean:message key="rule.timeEnd"/></td>
<td><html:text property="timeEndStr"/></td>
</tr>
<tr>
<td>
<html:submit styleClass="wpsButtonText"><bean:message key="application.submit"/></html:submit>
</td>
<td>
<input type="button" onclick="cancelOperation()" class="wpsButtonText" value="<bean:message key="application.cancel"/>" />
<html:link styleClass="cancelLink" page="/RuleList.do"></html:link>
</td>
</tr>
</table
</html:form>
my RuleList.jsp:
<html:form action="/CritRuleList.do" >
<table style="width: 100%;">
<tr>
<td><bean:message key="rule.levelId"/></td>
<td><html:select property=levelId">
<html:optionsCollection property="levels" value="clid" label="name" />
</html:select>
</td>
</tr>
<tr>
<td><bean:message key="rule.timeStart"/></td>
<td><html:text property="timeStartStr" /></td>
</tr>
<tr>
<td><bean:message key="rule.timeEnd"/></td>
<td><html:text property="timeEndStr" /></td>
</tr>
<tr>
<td colspan="2">
<html:submit styleClass="wpsButtonText"><bean:message key="application.search"/></html:submit>
<input type="button" onclick="cancelOperation(this)" class="wpsButtonText" value="<bean:message key="critrule.searchClear"/>" />
<html:link styleClass="cancelLink" page="/RuleResetSearchFilter.do"></html:link>
</td>
</tr>
</table>
</html:form>
It sounds very strange that a subclass method would be called in the case you've described. To find out why it's happening, you need to debug your code - put a breakpoint inside the if construct of the RuleSearchForm.getLevels() method and see if it is really called (and where did the call originate from).
Other than that, you could try to move the populating logic of levels away from the form(s) altogether, and instead do it in the action. So, in the RuleCreate action you would do something like this:
List levels = DAOClass.getLevels();
Collections.reverse(levels);
request.setAttribute("levels", levels);
This code is really hard to follow, and you still left out or changed some mappings (RuleCreate or CreateRule). My guess would be that you have a misunderstanding in which form declaration is used to populate the fields and the options collection. When you create a form like this
<html:form action="/RuleSave.do">
The values for the select box are taken from the form bean associated with the RuleSave action in your struts-config, not from the form of the action that forwarded to this jsp.
From http://struts.apache.org/1.2.x/userGuide/struts-html.html#form:
Renders an HTML element ... The form bean is located, and created if necessary, based on the form bean specification for the associated ActionMapping.
The [roblem has been resolved.
1. As Tommi said, I've inspected DAO code. DAO returns list of beans kept in static private field.
/**
* Add empty value. User should have an opportunity not to set value for this field.
* */
public Collection getLevels(){
if(levels == null || levels.size() == 0){
super.getLevels();
levels.add(0, new Level());
}
return levels;
}
So each time I add empty bean I do change that static list of objects. Terrible bug.
Params transferring. I've created struts1 app for WebSphere Portal. Portal doesn't have redirect, because I work with portlets, not with pages. That is why request parameters live in request unlit last jsp page is rendered. It has been also solved.