I need to build a chat using signalr and I am new in this.
So far I got only the chat by reading some others codes and tutorials and this is what I got:
on my ChatApp.Hubs I got the following code
public static class UserHandler
{
public static HashSet<string> ConnectedIds = new HashSet<string>();
}
public class ChatHub : Hub
{
public void Send(string name, string message)
{
// Call the addNewMessageToPage method to update clients.
Clients.All.addNewMessageToPage(name, message);
}
public override Task OnConnected()
{
UserHandler.ConnectedIds.Add(Context.ConnectionId);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
UserHandler.ConnectedIds.Remove(Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
}
and my view I copy past from a tutorial
#{
ViewBag.Title = "Chat";
}
<h2>Chat</h2>
<div class="container">
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" />
<input type="hidden" id="displayname" />
<ul id="discussion">
</ul>
</div>
#section scripts {
<!--Script references. -->
<!--The jQuery library is required and is referenced by default in _Layout.cshtml. -->
<!--Reference the SignalR library. -->
<script src="~/Scripts/jquery.signalR-2.1.0.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/signalr/hubs"></script>
<!--SignalR script to update the chat page and send messages.-->
<script>
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call back to display messages.
chat.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
// This optional function html-encodes messages for display in the page.
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
}
what I need now is to display all the connected users in the view
Appriciate your help
Thanks in advance
So, you pretty much either want to just store all 'Active' connections in some kind of database/storage or a static hashset/dictionary.
You save the ConnectionIds when the user connects and remove them when they disconnect :
Hub
public class ChatHub : Hub
{
static HashSet<string> CurrentConnections = new HashSet<string>();
public override Task OnConnected()
{
var id = Context.ConnectionId;
CurrentConnections.Add(id);
return base.OnConnected();
}
public override System.Threading.Tasks.Task OnDisconnected()
{
var connection = CurrentConnections.FirstOrDefault(x => x == Context.ConnectionId);
if (connection != null)
{
CurrentConnections.Remove(connection);
}
return base.OnDisconnected();
}
//return list of all active connections
public List<string> GetAllActiveConnections()
{
return CurrentConnections.ToList();
}
}
Client
I added a button and an unordered list.
HTML
<button id="show-all-connections">Show Connections</button>
<ul id="user-list">
</ul>
And added this javascript (using jQuery)
$("#show-all-connections").on("click", function () {
debugger;
chatHub.server.getAllActiveConnections().done(function (connections) {
$.map(connections, function (item) {
$("#user-list").append("<li>Connection ID : " + item + "</li>");
});
});
});
Hope this helps.
Update
In your scenario, I don't see any hooks into using a custom UserId Provider or anything, so you're going to have to ask the User for a User Name and save the Connection ID with that.
HTML
JavaScript
$("#add-connection").click(function () {
var name = $("#user-name").val();
if (name.length > 0) {
chatHub.server.connect(name);
}
else {
alert("Please enter your user name");
}
});
Hub
static List<Users> SignalRUsers = new List<Users>();
public void Connect(string userName)
{
var id = Context.ConnectionId;
if (SignalRUsers .Count(x => x.ConnectionId == id) == 0)
{
SignalRUsers .Add(new Users{ ConnectionId = id, UserName = userName });
}
}
public override System.Threading.Tasks.Task OnDisconnected()
{
var item = SignalRUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (item != null)
{
SignalRUsers.Remove(item);
}
return base.OnDisconnected();
}
Users.cs
public class Users
{
public string ConnectionId { get; set; }
public string UserName { get; set; }
}
This is psuedo code since I am not able to run this code at the moment. Hope it helps and gives you a clear enough direction.
Related
I am having a two tier application, with one being Windows Form application and other being Web Application with MVC.
Desktop Application has a SignalR Hub area which manages all client connected to it from Web App.
Hub Class
public delegate void ClientConnectionEventHandler(string clientId);
public delegate void ClientNameChangedEventHandler(string clientId, string newName);
public delegate void ClientInitializeEventHandler(string clientId);
public class StockTickerHub:Hub
{
static ConcurrentDictionary<string, string> _users = new ConcurrentDictionary<string, string>();
public static event ClientConnectionEventHandler ClientConnected;
public static event ClientConnectionEventHandler ClientDisconnected;
public static event ClientNameChangedEventHandler ClientNameChanged;
public static event ClientInitializeEventHandler ClientInitialized;
//Called when a client is connected
public override Task OnConnected()
{
_users.TryAdd(Context.ConnectionId, Context.ConnectionId);
ClientConnected?.Invoke(Context.ConnectionId);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
string username;
_users.TryRemove(Context.ConnectionId, out username);
ClientDisconnected?.Invoke(Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
public void SetUserName(string userName)
{
_users[Context.ConnectionId] = userName;
ClientNameChanged?.Invoke(Context.ConnectionId, userName);
}
public void InitializeGrid()
{
ClientInitialized?.Invoke(Context.ConnectionId);
}
}
When web client connects to Desktop App, it's being added and connected.
Inherited Hub Class
public class ClientGateway
{
private BindingList<ClientItem> _clients = new BindingList<ClientItem>();
frmMasterTicker frm;
public ClientGateway()
{
//Register to hub events
StockTickerHub.ClientConnected += StockTickerHub_ClientConnected;
StockTickerHub.ClientNameChanged += StockTickerHub_ClientNameChanged;
StockTickerHub.ClientDisconnected += StockTickerHub_ClientDisconnected;
StockTickerHub.ClientInitialized += StockTickerHub_ClientInitialized;
}
private void StockTickerHub_ClientInitialized(string clientId)
{
InitializeGrid(clientId);
}
private void StockTickerHub_ClientDisconnected(string clientId)
{
var client = _clients.FirstOrDefault(x => x.Id == clientId);
if (client != null)
{
_clients.Remove(client);
}
}
private void StockTickerHub_ClientNameChanged(string clientId, string newName)
{
//Update client's name if it's available
var client = _clients.FirstOrDefault(x => x.Id == clientId);
if (client != null)
{
client.Name = newName;
SetOperationLogMessage.AddLogMessage(this.ToString(), "", $"Client name changed. Id:{clientId}, Name:{newName}");
SendTestMessage();
}
}
private void StockTickerHub_ClientConnected(string clientId)
{
//Add client to the list
_clients.Add(new ClientItem() { Id = clientId, Name = clientId });
SetOperationLogMessage.AddLogMessage("ClientGateway", "StockTickerHub_ClientConnected", $"Client connected:{clientId}");
}
public void SendTestMessage()
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
hubContext.Clients.All.addMessage("Ticker Server", "Hello handshake from server.");
}
public void InitializeGrid(string connectionid)
{
if (_clients.Count > 0)
{
frm = (frmMasterTicker)Helper.GetOpenForm("frmMasterTicker");
//string msg = "Hello from server at " + DateTime.Now.ToString();
string msg = JsonConvert.SerializeObject(frm.GetInitializeDataFromGrid());
var hubContext = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
hubContext.Clients.Client(connectionid).initializeGrid(msg);
}
}
public void SendTickerData(object lstStock)
{
if (_clients.Count > 0)
{
//string msg = "Hello from server at " + DateTime.Now.ToString();
string msg = JsonConvert.SerializeObject(lstStock);
var hubContext = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
hubContext.Clients.Client("").getTickerData(msg);
}
}
}
Web Page Code
//Connect to SignalR server and get the proxy
function connect() {
$.connection.hub.url = url;
simpleHubProxy = $.connection.stockTickerHub;
if (simpleHubProxy) {
$.connection.hub.start().done(function () {
writeToLog("Connected...");
simpleHubProxy.server.setUserName("RMSAdmin");
RequestGridInitialData();
})
.fail(function () {
alert("Can't connect");
})
;
simpleHubProxy.client.addMessage = function (name, message) {
writeToLog(name + ":" + message);
}
simpleHubProxy.client.initializeGrid = function (message) {
dtSource = JSON.parse(message);
$("#grid").data("kendoGrid").dataSource.data(dtSource);
}
simpleHubProxy.client.getTickerData = function (message) {
writeToLog(message);
}
$.connection.hub.disconnected(function () {
writeToLog("Server disconnected.");
});
$.connection.hub.reconnecting(function () {
writeToLog("Server reconnecting...");
});
$.connection.hub.reconnected(function () {
writeToLog("Server reconnected...");
});
$.connection.hub.error(function (error) {
console.log('SignalR error: ' + error)
});
}
}
connect();
I can get the connection id from Hub.
Now I have a Windows Form. In which, I want to send data to exactly the same client who connected recently. I have a list of all clients connection with id. But within that, who connected recently and to whom I need to send data from Form, I am unable to do the progress with.
Following is a code try inside a Windows Form, which works, but it sends data to all connected client.
hubContext.Clients.All.getTickerData(JsonConvert.SerializeObject(tmpStock));
I want to send data only to that client who connects recently. How should I do that?
In the piece of code where you are sending the windows form data, you can call:
hubContext.Clients.Caller.getTickerData(JsonConvert.SerializeObject(tmpStock));
The .Caller will send the message to the client that invoked the method.
You can read more about it, in the oficial microsoft documentation, here: https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server#selectingclients
Edit: Since you don't have access to the .Caller method outside the hub you need to save the ClientId inside the hub and pass it the the outside class... SO there you will be able to call the caller client by id:
hubContext.Clients.Client(clientId).getTickerData(JsonConvert.SerializeObject(tmpStock));
I have to two separate asp.net projects on the same server. All correct SignalR nuget packages are installed as far as I know on both projects. One is the ChatServer and another is a ChatClient. Both are DotNetNuke projects.
The second project DOES know about signalR because OnConnected in Server project gets triggered when running client project and the client user is inserted as online (through the server OnConnected method).
When the sever comes online (by launching server project)the method in the client "getonlineusers" does not get triggered though.
But if I refresh the client, the server does show as offline. But I shouldnt have to refresh.
I am probably missing something not sure if I am creating a proxy correctly for the client project, any help would be greatly appreciated.
How do you connect a client separate project to the project that has the hub with SignalR?
In the ChatServer project:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
public class ChatSupportHub : Hub
{
private ChatServerManager CSM { get; set; }
private int UserID { get; set; }
//private UserInfo CurrentUser { get; set; }
public ChatSupportHub()
{
CSM = new ChatServerManager();
//CurrentUser = DotNetNuke.Entities.Users.UserController.Instance.GetCurrentUserInfo();
}
public override Task OnConnected()
{
int outNumber = -1;
Online user = new Online();
User dbUser = new User();
var userid = Context.QueryString["userid"];
bool success = Int32.TryParse(userid, out outNumber);
string connID = Context.ConnectionId;
if (success)
{
user.ID = outNumber;
dbUser = CSM.GetUser(outNumber);
UserID = outNumber;
}
user.ConnectionID = connID;
user.Type = dbUser.TypeID.HasValue ? CSM.GetUserType((int) dbUser.TypeID) : null;
user.ShowOnline = true;
CSM.AddOnlineUser(user);
var onlineUsers = CSM.GetOnlineUsers();
var clients = Clients.Caller;
string onlineUserJSON = JsonConvert.SerializeObject(onlineUsers);
clients.getonlineusers(dbUser.Name, onlineUserJSON);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
var username = Context.User.Identity.Name;
int userID = CSM.GetUserId(username);
CSM.RemoveOnlineUser(userID);
return base.OnDisconnected(stopCalled);
}
}
And the View index page of the ChatServer project:
<script src="~/myServertProjectPath/Scripts/jquery.signalR-2.4.1.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
$(function () {
// set up the hub connection
var hub = $.connection.chatSupportHub;
$.connection.hub.qs = "userid=" + #Model.CurrentUserInfo.UserID.ToString();
hub.client.getonlineusers = function (currentUsername, onlineUsers) {
if (onlineUsers) {
console.log('There are users online and one is: ' + currentUsername);
}
}
$.connection.hub
.start()
.done(function () {
})
});
And same thing in the separate client project for the view:
<script src="~/myClientProjectPath/Scripts/jquery.signalR-2.4.1.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
$(function () {
connect();
});
function connect() {
hub = $.connection.chatSupportHub;
$.connection.hub.qs = "userid=" + #Model.CurrentUserInfo.UserID.ToString();
hub.client.getonlineusers = function (currentUsername, onlineUsers) {
if (onlineUsers) {
console.log ("Hello from the separate project.");
}
}
$.connection.hub
.start()
.done(function () {
})
}
</script>
MyCustomControl.razor
<input type="text" id="#id" />
#code {
[Parameter]
public string id { get; set; }
}
Test.Razor
#page "/test"
<button #onclick="#addCompoment">add text box</button>
<div class="simple-list-list">
#if (componentListTest == null)
{
<p>You have no items in your list</p>
}
else
{
<ul>
#foreach (var item in componentListTest)
{
#item<br/>
}
</ul>
}
</div>
#functions {
private List<RenderFragment> componentListTest { get; set; }
private int currentCount { get; set; }
private string TxtExample { get; set; }
protected void OnInit()
{
currentCount = 0;
componentListTest = new List<RenderFragment>();
}
protected void addCompoment()
{
if(componentListTest==null)
{
componentListTest = new List<RenderFragment>();
}
componentListTest.Add(CreateDynamicComponent(currentCount));
currentCount++;
}
RenderFragment CreateDynamicComponent(int counter) => builder =>
{
try
{
var seq = 0;
builder.OpenComponent(seq, typeof(MyCustomControl));
builder.AddAttribute(++seq, "id", "listed-" + counter);
builder.CloseComponent();
}
catch (Exception ex)
{
throw;
}
};
}
After Adding the textbox dynamically,how to retrieve all input data from the textbox (after clicking on the submit button.)
How to interact with dynamic component and fetch Value.
MyCustomControl is component, Append in Test Razor Page.
for these component create an attribute like bind-value to get input field data given by user
There are a couple of solutions to this type of issue, depending on the general design of your app, constraints, and such like. The following solution is simple. Generally speaking, it involves passing the value of the added text box to a parent component to be saved in a list object. The parent component has a button that displays the list of text when clicked.
The following is the definition of the child component:
MyCustomControl.razor
<input type="text" #bind="#Value" id="#ID" />
#code {
private string _value;
public string Value
{
get { return _value; }
set
{
if (_value != value)
{
_value = value;
if (SetValue.HasDelegate)
{
SetValue.InvokeAsync(value);
}
}
}
}
[Parameter]
public string ID { get; set; }
[Parameter]
public EventCallback<string> SetValue { get; set; }
}
Usage in a parent component
<button #onclick="#addCompoment">add text box</button>
<div class="simple-list-list">
#if (componentListTest == null)
{
<p>You have no items in your list</p>
}
else
{
<ul>
#foreach (var item in componentListTest)
{
#item
<br />
}
</ul>
}
</div>
<p><button #onclick="#ShowValues">Show values</button></p>
#if (Display)
{
<ul>
#foreach (var value in values)
{
<li>#value</li>
}
</ul>
}
#code {
public void SetValue(string value)
{
values.Add(value);
}
private List<RenderFragment> componentListTest { get; set; }
private List<string> values = new List<string>();
private int currentCount { get; set; }
protected override void OnInitialized()
{
currentCount = 0;
componentListTest = new List<RenderFragment>();
}
private bool Display;
private void ShowValues()
{
if (values.Any())
{
Display = true;
}
}
protected void addCompoment()
{
if (componentListTest == null)
{
componentListTest = new List<RenderFragment>();
}
componentListTest.Add(CreateDynamicComponent(currentCount));
currentCount++;
}
RenderFragment CreateDynamicComponent(int counter) => builder =>
{
try
{
builder.OpenComponent(0, typeof(MyCustomControl));
builder.AddAttribute(1, "id", "listed-" + counter);
builder.AddAttribute(2, "SetValue", Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<Microsoft.AspNetCore.Components.EventCallback<System.String>>(Microsoft.AspNetCore.Components.EventCallback.Factory.Create<System.String>(this, this.SetValue )));
builder.CloseComponent();
}
catch (Exception ex)
{
throw;
}
};
}
Note:
Notice the SetValue attribute I've added to the CreateDynamicComponent's builder. This provides a Component Parameter to MyCustomControl of type EventCallback<string> which is assigned to the SetValue parameter property:
[Parameter]
public EventCallback<string> SetValue { get; set; }
And it is used (trigger the method which is also called SetValue in the parent component. You can change the name if you like) to pass the changed value from the child component to the parent component.
Use code instead of functions.
Note that I've made some modifications in your code: OnInitialized instead of OnInit (obsolete), sequence numbers should not created the way you do. Refer to this article written by Steve Sanderson ...
Hope this helps...
Index.cshtml
#{
ViewBag.Title = "Index";
}
<script src="~/Scripts/jquery.signalR-2.0.0.min.js"></script>
<script>
$(function () {
var connection = $.connection('/echo');
connection.received(function (data) {
$('#messages').append('<li>' + data + '</li>');
});
connection.start().done(function () {
$("#broadcast").click(function () {
connection.send($('#msg').val());
});
});
});
</script>
<input type="text" id="msg" />
<input type="button" id="broadcast" value="broadcast" />
<ul id="messages">
</ul>
startup.cs
using Microsoft.Owin;
using Owin;
using UI;
namespace UI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
MyConnection.cs
public class MyConnection : PersistentConnection
{
protected override Task OnConnected(IRequest request, string connectionId)
{
return Connection.Send(connectionId, "Welcome!");
}
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
return Connection.Broadcast(data);
}
}
I m new to signalR. When I start the project I get following error:
http://localhost:49820/echo/negotiate?clientProtocol=1.3&_=1383403303981 404 (Not Found)
What is the reason of this and how can I fix it? I cant find any solution about this problem. (.net framework 4.5 and signalR 2.0)
I can not debug your code at the moment, but I think you should consider two possible problems.
1) You missed "$.connection.hub" in your start hub function. And when you call received method from server side to client side, you need to add 'connection.client.received'. It should be like below:
$(function () {
var connection = $.connection('/echo');
connection.client.received(function (data) {
$('#messages').append('<li>' + data + '</li>');
});
$.connection.hub.start().done(function () {
$("#broadcast").click(function () {
connection.send($('#msg').val());
});
});
});
2) You need to map your persistent connection. I believe in signalR 1.x should be something like:
RouteTable.Routes.MapConnection<MyConnection>("echo", "/echo");
But in SignalR 2.0 you need to do the following in your hub Startup class. Below is just an example.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR<MyConnection>("/echo");
}
I have a POJO named "FlashCard" which has a field named "links" which is collection (set) of Link objects. When I submit a FORM to my Action all the POJO fields are populated with values from the form except the collection of "links". I have no idea why this isn't getting populated.
Any advice on how to resolve this problem or how to better troubleshoot it would be much appreciated.
Also, my POJO's collection is a Set. Does it matter (or complicate things) that I'm using a Set and not a List?
I'm including a simplified version of my code below.
Here's my POJO:
public class FlashCard implements java.io.Serializable {
private int flashCardId;
private String question;
private String answer;
private Set<Link> links = new HashSet<Link>(0);
public FlashCard() {
}
public FlashCard(String question, String answer) {
this.question = question;
this.answer = answer;
}
public FlashCard(String question, String answer, Set<Link> links) {
this.question = question;
this.answer = answer;
this.links = links;
}
public int getFlashCardId() {
return this.flashCardId;
}
public void setFlashCardId(int flashCardId) {
this.flashCardId = flashCardId;
}
public String getQuestion() {
return this.question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getAnswer() {
return this.answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
public Set<Link> getLinks() {
return this.links;
}
public void setLinks(Set<Link> links) {
this.links = links;
}
}
Here's the POJO for the Link object:
public class Link implements java.io.Serializable {
private int linkId;
private String url;
private Set<FlashCard> flashcards = new HashSet<FlashCard>(0);
public Link() {
}
public Link(String url) {
this.url = url;
}
public Link(String url, Set<FlashCard> flashcards) {
this.url = url;
this.flashcards = flashcards;
}
public int getLinkId() {
return this.linkId;
}
public void setLinkId(int linkId) {
this.linkId = linkId;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public Set<FlashCard> getFlashcards() {
return this.flashcards;
}
public void setFlashcards(Set<FlashCard> flashcards) {
this.flashcards = flashcards;
}
}
Here's the relevant part of the Action
public class FlashCardAction extends FlashCardsAppBaseAction implements ModelDriven<FlashCard>, Preparable, SessionAware {
static Logger logger = Logger.getLogger(FlashCardAction.class);
FlashCard flashCard = new FlashCard();
Map <String,Object> httpSession;
Session session;
FlashCardPersister fcPersister;
public Map<String, Object> getHttpSession() {
return httpSession;
}
public FlashCard getFlashCard() {
return this.flashCard;
}
public void setFlashCard(FlashCard flashCard) {
this.flashCard = flashCard;
}
public void validate() {
logger.debug("Entering validate()");
if ( flashCard.getQuestion().length() == 0 ){
addFieldError("flashCard.question", getText("error.flashcard.question"));
}
if ( flashCard.getAnswer().length() == 0 ) {
addFieldError("flashCard.answer", getText("error.flashcard.answer"));
}
}
public String saveOrUpdate() {
logger.debug("Entering saveOrUpdate()");
// assume we'll fail
boolean result = false;
// are we creating a New Flash Card or Updating and existing one
// for now, let's assume we are creating a New Flash Card
boolean newFlashCard = true;
// if this is an Update of an existing Flash CArd then we'll have a Flash Card Id other than 0
if (this.flashCard.getFlashCardId() != 0) {
newFlashCard = false;
}
try {
result = fcPersister.saveOrUpdateFlashCard(this.flashCard, session);
// did we save a new FlashCard successfully?
if (result == true && newFlashCard) {
logger.debug("Flash Card created successfully");
this.addActionMessage(getText("actionmessage.flashcard.created"));
}
// did we update an existing Flash Card successfully?
else if (result == true && newFlashCard == false) {
logger.debug("Flash Card updated successfully");
this.addActionMessage(getText("actionmessage.flashcard.updated"));
}
// such a failure
else {
logger.error("unable to create or update FlashCard");
return "error";
}
return "success";
} catch (Exception e) {
logger.error("Exception in createFlashCard():", e);
return "error";
}
}
#Override
public FlashCard getModel() {
return this.flashCard;
}
#Override
public void setSession(Map<String, Object> httpSession) {
this.httpSession = httpSession;
}
#Override
public void prepare() throws Exception {
logger.debug("Entering prepare()");
// get a handle to a Hibernate session
session = getHibernateSession();
// get a handle to the FlashCard persistance utility class
fcPersister = new FlashCardPersister();
}
}
And lastly here's the JSP
<%#page import="com.opensymphony.xwork2.ActionContext"%>
<%#page import="com.opensymphony.xwork2.ActionSupport"%>
<%# page contentType="text/html; charset=UTF-8"%>
<%# taglib prefix="s" uri="/struts-tags"%>
<%# taglib prefix="sjr" uri="/struts-jquery-richtext-tags"%>
<h3><s:text name="label.flashcard.title"/></h3>
<s:actionerror theme="jquery" />
<s:actionmessage theme="jquery"/>
<s:fielderror theme="jquery"/>
<s:form action="saveOrUpdate" method="post">
<s:hidden name="flashCard.flashCardId" />
<s:textfield name="flashCard.question" key="label.flashcard.question" size="66" />
<sjr:tinymce
id="flashCard.answer"
name="flashCard.answer"
key="label.flashcard.answer"
rows="20"
cols="50"
editorTheme="simple"
/>
<s:textfield name="flashCard.links.url" key="label.flashcard.link" size="66" />
<tr>
<td>
<s:submit label="label.flashcard.submit" align="center" theme="simple" />
</td>
<td>
<s:submit key="label.flashcard.cancel" name="redirectAction:list" theme="simple" />
</td>
</tr>
</s:form>
<%((ActionSupport)ActionContext.getContext().getActionInvocation().getAction()).clearErrorsAndMessages();%>
First of all I don't think you can use Set here, because Sets are unordered and you can't get an item from a set by an index or key like List and Map. The only way is to iterate through the set and get the items.
Second assuming you're using a collection other than set, in:
<s:textfield name="flashCard.links.url" key="label.flashcard.link" size="66"/>
You try to set the value of the text field to url field of links which is a collection and doesn't have such a field. So you need to get the specific item from the collection you're editing and pass the value. Like:
<s:textfield name="flashCard.links[0].url" key="label.flashcard.link" size="66"/>
But since you can't get the specific item you are editing I suggest you create a link field in your Action and set the updated link to it. Then you can perform a logic to relace the updated link with obsolete one in you flashcards. Hope this helps.
Since you are using modeldriven and the model is FlashCard, i think the following
<sjr:tinymce
id="flashCard.answer"
name="flashCard.answer"
key="label.flashcard.answer"
rows="20"
cols="50"
editorTheme="simple"/>
should be changed to
<sjr:tinymce
id="flashCard.answer"
name="answer"
key="label.flashcard.answer"
rows="20"
cols="50"
value="answer"
editorTheme="simple"/>
the name field should be given without the prefix flashcard.also you should provide the 'value' attribute in order for it to be pre-populated.