Event of StatelessLinks in WebMarkupContainer don't fire - wicket

I have a form with a StatelessLink ("Delete profile?"). When this link is clicked a WebMarkupContainer is made visible containing two more links ("Really delete profile!" and "Cancel").
Java:
private StatelessLink deleteProfileWarningLink;
private WebMarkupContainer deleteProfileContainer;
private StatelessLink deleteProfileLink;
private StatelessLink deleteProfileCancelLink;
public MyForm() {
...
deleteProfileWarningLink = new StatelessLink("profileDeleteWarningLink") {
#Override
public void onClick() {
deleteProfileWarning();
}
};
deleteProfileContainer = new WebMarkupContainer("deleteProfileContainer");
deleteProfileContainer.setVisible(false);
deleteProfileLink = new StatelessLink("reallyDeleteProfileLink") {
#Override
public void onClick() {
deleteProfile();
}
};
deleteProfileCancelLink = new StatelessLink("cancelDeleteProfileLink") {
#Override
public void onClick() {
cancelDeleteProfile();
}
};
...
add(deleteProfileWarningLink);
deleteProfileContainer.add(deleteProfileLink);
deleteProfileContainer.add(deleteProfileCancelLink);
add(deleteProfileContainer);
}
And some HTML:
<fieldset>
<div wicket:id="deleteProfileContainer" class="deleteProfil">
<div class="wrapper">
<a wicket:id="reallyDeleteProfileLink" class="button delete">Really delete profile!</a>
<a wicket:id="cancelDeleteProfileLink" class="button cancel">Cancel</a>
</div>
</div>
<span class="unitA">
<a wicket:id="profileDeleteWarningLink" class="button delete">Delete profile?</a>
</span>
</fieldset>
However the events of the StatelessLinks in the WebMarkupContainer never fire.

I think you should optimize this with some custom JavaScript.
The idea is the following: Wicket generates all three buttons in the initial version of the page. The first button is initially visible and uses JavaScript to show the initially hidden (CSS, display:none) container with the other two buttons. The rest is as it is now.
Recently we discussed something related to your problem in dev# mailing list:
http://markmail.org/message/dkmxw4urqm444ryc

Related

How to submit a form multiple times (asp.net core razor)

I am trying to create a form that can be submitted multiple times with different information, while retaining a common value in one field.
I have a list view from a SQL table in ASP.NET Core Razor that is a list of construction projects. For each row in the list I have a link that goes to a "create" template page where users can create a bid entry for the project which is stored in a different table. The Project Number is assigned to a route value (asp-route-Number = "the project number from the previous list")and populates a hidden field in the "create new bid" form.
Using the default code for the razor page, everything works great. You click submit and are taken back to the list of projects.
What I want to do is have another option on the "create new bid" form that will allow you to save and enter another bid for the same project. I created another button and handler to do this but I am stuck on actually implementing it. If I use return Page() the form posts and the page is returned with route data intact, but the text fields still contain the previous data and the drop-down list is empty. If I use return RedirectToPage(CreateNewBid, Route data) the form posts but the route data does not seem to be passed along and creates a null value error.
This is the link from the Projects list (inside the foreach table), which takes you to the "Create Bid" form and works fine.
<a asp-page="CreateBid" asp-route-Number="#item.ProjectNumber" asp-route-opwid="#item.Id">New Bid</a>
The Create Bid form has the following to submit and create another entry
int num = int.Parse(Request.Query["Number"]);
int idnum = int.Parse(Request.Query["opwid"]);
<input type="submit" value="Save and enter another"
asp-page-handler="Another" asp-route-opwid="#idnum"
asp-route-Number="#num" class="btn btn-primary"/>
And the handler:
public async Task<IActionResult> OnPostAnotherAsync(int Number, int opwid)
{
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
return Page();
//return RedirectToPage("./CreateBid", (Number == num, opwid == idnum));
}
I have also tried several things in the route parameters (as opposed to using the variables) in the "Redirect to Page" and nothing seems to work.
Is there an easier way, or am I just missing something?
This is the cshtml file:
#page
#model Authorization_AD.Pages.GenSvc.BidEntry.CreateBidModel
#{
ViewData["Title"] = "CreateBid";
}
#{ int num = int.Parse(Request.Query["Number"]);
int idnum = int.Parse(Request.Query["opwid"]);
}
<h1>Create Bid</h1>
<h4>OPW number #num</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input asp-for="OpwBids.OpwProject" value="#idnum" hidden class="form-control" />
</div>
<div class="form-group">
<label asp-for="OpwBids.OpeningDate" class="control-label"></label>
<input asp-for="OpwBids.OpeningDate" class="form-control" />
<span asp-validation-for="OpwBids.OpeningDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="OpwBids.Contractor" class="control-label"></label>
<select asp-for="OpwBids.Contractor" class="form-control" asp-items="ViewBag.Contractor">
<option disabled selected>--- SELECT ---</option>
</select>
</div>
<div class="form-group">
<label asp-for="OpwBids.BidAmount" class="control-label"></label>
<input asp-for="OpwBids.BidAmount" class="form-control" />
<span asp-validation-for="OpwBids.BidAmount" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save and enter another"
asp-page-handler="Another" asp-route-opwid="#idnum"
asp-route-Number="#num" class="btn btn-primary"/>
<input type="submit" value="Save and return to list" asp-page-handler="Done" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
This is the C# file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Authorization_AD.Models;
namespace Authorization_AD.Pages.GenSvc.BidEntry
{
public class CreateBidModel : PageModel
{
private readonly Authorization_AD.Models.OPWContext _context;
public CreateBidModel(Authorization_AD.Models.OPWContext context)
{
_context = context;
}
public IActionResult OnGet()
{
ViewData["Contractor"] = new SelectList(_context.Contractors, "Id", "ContractorName");
ViewData["OpwProject"] = new SelectList(_context.MainProjectsListing, "Id", "ProjectNumber");
return Page();
}
[BindProperty]
public OpwBids OpwBids { get; set; }
public async Task<IActionResult> OnPostDoneAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
public async Task<IActionResult> OnPostAnotherAsync(int Number, int opwid)
{
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
return Page();
//return RedirectToPage("./CreateBid", (Number == OpwBids.OpwProjectNavigation.ProjectNumber, opwid == OpwBids.OpwProject));
}
}
}
You can add a property to your page that will be used to bind the value of the clicked button.
public class CreateBidModel : PageModel {
//...
//Add this property to your page.
[BindProperty]
public string Button {get;set;}
public void OnGet(int number,string opwid){
//Set the number and opwid to the target properties
}
public Task<IActionResult> OnPostAsync(){
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
if(Button == "finish"){
return RedirectToPage("./Index");
}
else {
return RedirectToPage("./CreateBid", (Number == OpwBids.OpwProjectNavigation.ProjectNumber, opwid == OpwBids.OpwProject));
}
}
}
To the view you need to add two buttons that have the same name and that value will be mapped to the Button property.
<form method="post">
... Other content goes here
<button name="#Html.NameFor(m => m.Button)" value="another">Create another</button>
<button name="#Html.NameFor(m => m.Button)" value="finish">Finish</button>
</form>
The value of the clicked button will be parsed to the Button property of the Pagemodel. Based on the value you can decide how to further handle the response of the request (Finish / Create another one in your case).
Thanks for everyone's help. I got it to do what I want by adding the following to the "OnPostAnotherAsync" task:
public async Task<IActionResult> OnPostAnotherAsync(int Number, int opwid)
{
if (!ModelState.IsValid)
{
return Page();
}
_context.OpwBids.Add(OpwBids);
await _context.SaveChangesAsync();
ViewData["Contractor"] = new SelectList(_context.Contractors, "Id", "ContractorName");
ModelState.SetModelValue("OpwBids.BidAmount", new ValueProviderResult(string.Empty, CultureInfo.InvariantCulture));
ModelState.SetModelValue("OpwBids.Contractor", new ValueProviderResult(string.Empty, CultureInfo.InvariantCulture));
return Page();
}
After the "Save Changes" I needed to re-load the view data for the "Contractor" drop down list. Then it was just a matter of clearing the form fields before returning the page.

Returning object details in the View based on the object ID ASP.net core MVC

I'm building online catalog for phones, I have two controller one for phone Catalog and second for phone's details.With catalog everything is fine, now my goal is to see phone details after clicking on the phone's name or photo in the catalog.I think that with phone's id, its easier to solve this task, also I use repository pattern.
This Catalog' s Controller:
public class PhonesCatalog : Controller
{
private readonly IPhoneRepository _repository;
public int PageSize = 6;
public PhonesCatalog(IPhoneRepository repository)
{
_repository = repository;
}
[HttpGet]
public IActionResult Catalog(int productPage = 1)
=> View(new ProductsListViewModel
{
Phones = _repository.Phones
.OrderBy(x => x.PhoneId)
.Skip((productPage -1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = productPage,
ItemsPrePage = PageSize,
TotalItems = _repository.Phones.Count()
}
});
}
The repository pattern:
public interface IPhoneRepository
{
IQueryable<Phone> Phones { get; }
Phone GetPhoneById(int id);
}
public class EfMobileStoreRepository : IPhoneRepository
{
private readonly MobileStoreCatalogContext _context;
public EfMobileStoreRepository(MobileStoreCatalogContext context)
{
_context = context;
}
public IQueryable<Phone> Phones => _context.Phones;
public Phone GetPhoneById(int id) => _context.Phones
.FirstOrDefault(p => p.PhoneId == id);
}
and here is Phone detail controller and view:
public class PhonesDetails : Controller
{
private readonly IPhoneRepository _repository;
public PhonesDetails(IPhoneRepository repository)
{
_repository = repository;
}
[HttpGet]
public IActionResult Details(int id)
{
return View(_repository.GetPhoneById(id));
}
}
#model Mobile_Store_Catalog_wandio.Models.Phone
<h4>Phone Details</h4>
<div class="row">
<p>#Model.PhoneName</p>
<p>#Model.Manufactor</p>
<p>#Model.OperationSystem</p>
<p>#Model.Processor</p>
<p>#Model.Memory</p>
<p>#Model.ScreenResolution</p>
<p>#Model.Size</p>
<p>#Model.Wight</p>
</div>
Here Catalog View:
#model ProductsListViewModel
<h1>Phone Catalog</h1>
<div class="container-fluid">
<div class="container-fluid">
<div class="row">
<div class="col-md-3 btn-group-vertical text-center">
Filter
Search
</div>
<div class=" row col-md-9">
#foreach (var p in Model.Phones)
{
<div class=" col-md-4 border border-dark">
<a href="#Url.Action("Details", "PhonesDetails")">
<img class="img-fluid" src="/Images/#p.ImageName"/>
</a>
<p class="text-center container">#p.PhoneName</p>
<p class="text-white text-center bg-success">#p.Price.ToString("C2")</p>
</div>
}
</div>
</div>
</div>
</div>
<div class="row">
<div page-model="#Model.PagingInfo" page-action="Catalog" page-classes-enabled="true"
page-class="btn" page-class-normal="btn-outline-dark"
page-class-selected="btn-primary" class="btn-group-toggle m-1 al">
</div>
</div>
and here is the ProductViewList code:
public class ProductsListViewModel
{
public IEnumerable<Phone> Phones { get; set; }
public PagingInfo PagingInfo { get; set; }
}
Problem is that when I'm clicking on the phone image,but it takes only first id of phone and returns its detail,it doesn't matter which image is clicked, but I need to return details by id, not only first id Phone detail.
What I'm doing wrong here, can anyone help me?
You can change your code like this:
<a asp-controller="PhonesDetails" asp-action="Details" asp-route-id="#p.Id">
<img class="img-fluid" src="/Images/#p.ImageName"/></a>

Binding in Razor page with Kendo

I'm trying to get to grips with kendo binding in MVVM.
I have a Razor page that looks like this...
Index.cshtml
#page
#model IndexModel
#{
ViewData["Title"] = "Index";
}
<div id="frm">
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
<div class="form-group">
<label><input type="text" class="form-control" data-bind="value: Username"/></label>
</div>
<button type="submit" class="btn btn-primary">Click</button>
}
<label>
<input type="text" class="form-control" data-bind="value: Username" />
</label>
</div>
<script>
var raw = #Html.Raw(Model.Me.ToJson());
var vm = new kendo.observable(raw);
kendo.bind($("#frm"), vm);
</script>
Index.cshtml.cs...
public class IndexModel : PageModelBase
{
[BindProperty]
public Person Me { get; set; }
public void OnGet()
{
Me = new Person { Username = "Bobby Brown" };
}
public void OnPost()
{
var p = Me;
p.Username += ".";
}
public class Person
{
public string Username { get; set; }
public string ToJson() => JsonConvert.SerializeObject(this);
}
}
When I render the page, the 2 inputs are, properly bound to the passed in value from the server-side model.
When I change the value in one of the inputs client-side and change focus, the other input changes.
I expect all of this.
When I click the button, the control returns to the server and executes the code in OnPost().
What doesn't happen is for Me to be set to something other than null.
I've tried it as is shown above,
I've tried refactoring the OnPost() method to OnPost(Person me) but me isn't set.
I've tried assessing the Request.Form object but there is nothing there.
I'm sure it must be simpler than I'm trying to make it.
Can anyone offer any advice about that I'm doing wrong, please?
Basically, I'm dim.
I worked it out. I was trying to fit a square peg into a round hole.
I added a hidden input in the form with the name Me to match the property name I was binding to.
I changed the button to a regular button (from a submit button) and added an onClick handler that did this...
function submitForm() {
$("#Me").val({ Username: vm.get("Username") });
$("#frm").submit();
}
And "lo, there was light".
Thanks for listening

modal from ng-bootstrap integrate with sortable component from ng2-bootstrap

I have a button that trigger a modal using ng-bootstrap modal
<button (click)="openModal()">Open</button>
the template of modal.html with sortable component inside is:
<template #modalContent let-c="close" let-d="dismiss">
<bs-sortable #sortableComponent [(ngModel)]="array
[itemTemplate]="itemTemplate"></bs-sortable>
</template>
<template #itemTemplate let-item="item" let-index="index">
<div>{{item | json}}
<span class="fa fa-trash" (click)="removeItem(array,index)"></span>
</div>
</template>
the class will be:
import { NgbModal } from "#ng-bootstrap/ng-bootstrap";
export class sortableModal{
#Input() public array: [];
#ViewChild("modalContent") public modalContent: NgbModalModule;
#ViewChild("sortableComponent") sortableComponent: SortableComponent;
constructor( public modalService: NgbModal ){
}
openModal(){
this.modalService.open(this.modalContent);
}
removeItem(arr,i){
if(i===undefined) i = -1;
arr.splice(i,1);
this.sortableComponent.writeValue(arr);
//this.sortableComponent is undefined; why is that?
}
}
I still don't know why this is happening. But I solved it by wrap ng2-bootstrap sortable component into your own component, MySortableComponent, which has sortable component in. It works.

how to get current page in wicket

i am trying to create dynamic navigation links in which current page link should be highlighted but i am unable to get desired outcome. i am using listview to display my menu items but unable to highlight the current page link.
please suggest necessary changes
public class SearchPage extends WebPage implements Serializable {
private static final long serialVersionUID = 1L;
Logger log = Logger.getLogger(SearchPage.class);
public SearchPage() {
List<HeaderListItems> headerPOJOItems = new ArrayList<HeaderListItems>();
HeaderListItems searchHLI = new HeaderListItems();
searchHLI.setLabel("Search");
searchHLI.setDestPage(SearchPage.class);
headerPOJOItems.add(searchHLI);
HeaderListItems jobsHLI = new HeaderListItems();
jobsHLI.setLabel("Jobs");
jobsHLI.setDestPage(Jobs.class);
headerPOJOItems.add(jobsHLI);
HeaderListItems urlHLI = new HeaderListItems();
urlHLI.setLabel("URL");
urlHLI.setDestPage(URL.class);
headerPOJOItems.add(urlHLI);
HeaderListItems syssettingsHLI = new HeaderListItems();
syssettingsHLI.setLabel("System Settings");
syssettingsHLI.setDestPage(Settings.class);
headerPOJOItems.add(syssettingsHLI);
HeaderListItems usersHLI = new HeaderListItems();
usersHLI.setLabel("Users");
usersHLI.setDestPage(User.class);
headerPOJOItems.add(usersHLI);
HeaderListItems logoutHLI = new HeaderListItems();
logoutHLI.setLabel("Logout");
logoutHLI.setDestPage(WebApp.get().getHomePage());
headerPOJOItems.add(logoutHLI);
add(new ListView("headerlistview", headerPOJOItems) {
#Override
protected void populateItem(ListItem item) {
final HeaderListItems headerlistitems = (HeaderListItems) item
.getModelObject();
log.info("Label: " + headerlistitems.getLabel() + " dest: "
+ headerlistitems.getDestPage());
Link newlink = new Link("newlink") {
#Override
public void onClick() {
setResponsePage(headerlistitems.getDestPage());
}
};
newlink.add(new Label("newlabel", headerlistitems.getLabel()));
newlink.add(new AttributeModifier("class",
new AbstractReadOnlyModel() {
#Override
public Object getObject() {
// TODO Auto-generated method stub
return getPage().getClass().equals(
headerlistitems.getDestPage()
.getClass()) ? "activeitem"
: AttributeModifier.VALUELESS_ATTRIBUTE_REMOVE;
}
}));
item.add(newlink);
}
});
}
List is list of pojo item. my pojo has two fields label(String) and destPage(Class)
My Mark-up:
<html xmlns:wicket="http://wicket.apache.org">
<head>
<title>Search Page</title>
<link href="css/design.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div id="container">
<div class="header">
<div class="header_tab1">
<p align="center">Logo</p>
</div>
<div class="header_tab2"> </div>
<div class="header_tab3">
<table width="100%">
<tr>
<td wicket:id="headerlistview">
<ul>
<li><a href="#" wicket:id="newlink"><span
wicket:id="newlabel"></span></a></li>
</ul>
</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>
I have a CSS class activeitem which i am using in attribute modifier
Thanks in advance
Change the class comparison to:
return getPage().getClass().equals(
headerlistitems.getDestPage()) ? "activeitem" : AttributeModifier.VALUELESS_ATTRIBUTE_REMOVE;
The next step would be to extract the navigation into its own panel, so you can reuse it in your pages.
Good luck.