So heres how this works so far,the index page allows you to create a game. Games are then posted onto /data/games ( Gamelist.html). I want to have a button that permanently deletes the games from the list. I currently have a remove button but it doesnt do anything.
Index( create games from here)
<html>
<head>
<title>Planning Poker</title>
<style>
.inlinetext {
display: inline;
}
</style>
<script src="Scripts/jquery-2.1.1.js"></script>
<script src="Scripts/knockout-3.1.0.js"></script>
<script type="text/javascript">
$(function () {
$('#button').on('click', function (data) {
$.post('data/games/create/?title=5', function (d) { console.log(d) });
})
});
</script>
</head>
<body>
<h3 class='inlinetext'> Create Game: </h3>
<input type="text" id="testtext" name="ime">
<button id="button" >Create</button>
</body>
</html>
Controller
using PlanningPoker.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace PlanningPoker.Controllers
{
public class GMController : ApiController
{
private static List<Game> games = new List<Game>() {
new Game() {
ID = Guid.NewGuid(),
Title = "D&D"
}
};
[Route("data/games")]
public IEnumerable<Game> GetAllGames() {
return games;
}
[Route("data/games/create"), HttpPost]
public Guid CreateGame(string title) {
Game g = new Game() {
ID = Guid.NewGuid(),
Title = title
};
games.Add(g);
return g.ID;
}
}
}
Gamelist(html)
<title>Game List</title>
<script src="Scripts/jquery-2.1.1.js"></script>
<script src="Scripts/knockout-3.1.0.js"></script>
<script src="Gamelist.js"></script>
</head>
<body>
<h4>Games</h4>
<ul data-bind="foreach: $data.games">
<li>
Game <span data-bind="text: $index"> </span>:
<span data-bind="text: Title"> </span>
Remove
</li>
</ul>
<button data-bind="click: addGame">Add</button>
</body>
</html>
Gamlist (javascript)
function AppViewModel() {
var self = this;
self.games = ko.observableArray([]);
$.getJSON("/data/games", function (d) {
self.games(d);
});
self.addGame = function () {
self.game.push({ name: "Created On " + new Date() });
};
self.removeGame = function () {
self.game.remove(this);
}
}
$(function () {
ko.applyBindings(new AppViewModel());
});
Knockout will automatically pass back the current item as a parameter in the click event binding...
self.removeGame = function(game) {
self.games.remove(game);
}
Related
I have controller for changing website language, saving cookie and returning url.
`
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
namespace Website.Controllers;
public class CultureController : Controller
{
[HttpPost]
public IActionResult SetCulture(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddDays(365) }
);
return LocalRedirect(returnUrl);
}
}
`
And in View I need create html list for better user experience but I don't understand how to change from 'form' to 'list' or how to submit changes and return url
`
#using Microsoft.AspNetCore.Localization
#using Microsoft.Extensions.Options
#inject IOptions<RequestLocalizationOptions> LocalizationOptions
#{
var requestCulture = Context.Features.Get<IRequestCultureFeature>();
var cultureItems = LocalizationOptions.Value.SupportedUICultures
.Select(c => new SelectListItem { Value = c.Name, Text = c.EnglishName })
.ToList();
var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~/" : $"~{Context.Request.Path.Value}{Context.Request.QueryString}";
}
<!-- FROM FORM -->
<div class="language">
<form asp-controller="Culture" asp-action="SetCulture" asp-route-returnUrl="#returnUrl" class="form-horizontal nav-link text-dark">
<select name="culture"
onchange="this.form.submit();"
asp-for="#requestCulture.RequestCulture.UICulture.Name"
asp-items="cultureItems">
</select>
</form>
</div>
<!-- TO LIST -->
<div class="language-toggle">
<i class="fas fa-language"></i>
<ul class="language-menu">
#foreach (var item in LocalizationOptions.Value.SupportedUICultures)
{
<li>#item.Name.ToUpper()</li>
}
</ul>
</div>
`
I tried with anchor tag helper but without success
output
Output
I can get current url in view and append ?culture=en and that changes language and stays on current page but does not save cookie so every time user goes to different page website is in native language not in user selected language.
You can achieve that with something like this:
<head>
<script type="text/javascript">
function submitCulForm(val) {
document.getElementById("cultureVal").value = val;
var hh = document.getElementById("cultureForm");
hh.submit();
return false;
}
</script>
</head>
Then
<form asp-controller="Culture" id="cultureForm" asp-action="SetCulture" asp-route-returnUrl="#returnUrl" class="form-horizontal nav-link text-dark">
<input id="cultureVal" type="hidden" name="culture" value="-">
<div class="language-toggle">
<i class="fas fa-language"></i>
<ul class="language-menu">
#foreach (var item in LocalizationOptions.Value.SupportedUICultures)
{
<li>#item.Name.ToUpper()</li>
}
</ul>
</div>
</form>
If you try to pass the value with herf,you shouldn't add [HttpPost] Attribute on your controller.
I tried with the codes in your controller,it works well,I'll show what I've tried and hopes it could help
in View:
<div>
<a asp-action="SetCulture" asp-route-culture="zh-CN">zh-CN</a>
<a asp-action="SetCulture" asp-route-culture="en-US">en-US</a>
</div>
<script>
var cookie = document.cookie
console.log(cookie)
</script>
in Controller:
public IActionResult SetCulture(string culture)
{
if (culture != null)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddDays(365) });
return RedirectToAction("Create");
}
return BadRequest();
}
Configure in startup:
services.AddControllersWithViews()
.AddDataAnnotationsLocalization(
options =>
{
options.DataAnnotationLocalizerProvider = (type, factory) =>
factory.Create(typeof(SharedResources));
});
...............
var supportedCultures = new[] { "en-US", "zh-CN" };
var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures[0])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);
created an empty class called SharedResources
and the resourcefile:
The Result:
It just performed as the document,and if you tried with mulipule providers,you could try to change request culture providers order which has been mentioned in the document
The objective is to filter the display of list elements and corresponding markers.I'm unable to understand what is wrong with the logic. The search input should filter and when you undo/cancel the search input then the list should reappear with the markers.
HTML:
enter <html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> Neighborhood Map</title>
<link href="style.css" rel="stylesheet">
<script src="js/jquery-3.2.1.js"></script>
<script src="js/knockout-3.4.0.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div id="sidebar" class="col-xs-12 col-sm-5 col-md-3">
<h1 id="header">Chennai City Cultural Hubs</h1>
<div class="search-box">
<input class="text-search" type="text" placeholder="Enter here" data-
bind="textInput: query">
</div>
<div class= "list-box">
<div class="menu" data-bind="foreach: filteredItems">
<a class="menu-item"data-bind="text: title, click: $parent.setLoc" >
</a>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6 col-md-8">
<div id="map"></div>
</div>
</div>
<script src="js/app.js"></script>
</body>
</html>
JS:
function appViewModel() {
var self = this;
this.query = ko.observable('');
var filter = self.query().toLowerCase();
this.locationArray = ko.observableArray([]);
locations.forEach(function (item) {
self.locationArray().push(item);
});
self.setLoc = function (clickedLoc) {
var clickedData = clickedLoc.marker;
google.maps.event.trigger(clickedData, 'click')
};
this.filteredItems = ko.computed(function () {
if (!this.filteredItems) {
return self.locationArray();
} else {
return ko.utils.arrayFilter(self.locationArray(), function (item) {
var result = (item.title.toLowerCase().indexOf(filter) > -1)
item.marker.setVisible(result);
return result;
});
}
}, self);
};
ko.applyBindings(new appViewModel());
An explanation and solution would be very helpful.
Issues:
Your filter variable is not observable, so it won't update after instantiation. It's always an empty string, since by the time you assign it, query() returns "".
Checking for !this.filteredItems in the computed does not do anything, because it will never be false. (filteredItems is a ko.computed instance, which will evaluate to true)
Solution
You can rewrite your filteredItems to:
this.filteredItems = ko.computed(function () {
var q = this.query().toLowerCase();
if (!q) {
// TODO (?): reset all `setVisible` props.
return this.locationArray();
}
return this.locationArray()
.filter(function(item) {
var passedFilter = item.title.toLowerCase().indexOf(q) > -1;
item.marker.setVisible(passedFilter);
return passedFilter;
});
}, self);
By calling query you create a dependency to any changes in the search input. By calling locationArray you ensure updates when the data source changes. Note that you'll need to make sure your setVisible logic executes, even when you clear the query...
Remarks & tips
If you want, you can swap out Array.prototype.filter with ko.utils.arrayFilter, but the .filter method is widely supported by now.
You can create more (pure) computeds to separate logic. (E.g.: const lowerQuery = ko.pureComputed(() => this.query().toLowerCase()))
I wouldn't call setVisible in the filter since it's an unexpected side effect. Why not make it computed as well?
I've generated a <select> dropdown menu on my view page with data from a stored procedure. Using JavaScript, I've accessed the selected <option> and saved the text into a variable. Now I need to send that variable back to my controller so I can use it as a parameter in another stored procedure.
Here is my Controller, titled "EventsController.cs"
public ActionResult Dropdown(string text)
{
ViewData["ids"] = db.uspGetAllEventIds().ToArray();
Console.WriteLine(text);
return View();
}
So you can see I run the 1st stored procedure and send it to the view.
Here's what happens in the view:
#model IEnumerable<Heatmap.Models.Event>
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
<script>
function showValue()
{
var list = document.getElementById("EventId");
var text = list.options[list.selectedIndex].text;
alert(text);
}
</script>
</head>
<body>
<div>
#using (Html.BeginForm("", "", FormMethod.Post))
{
<select id="EventId">
#{
var ids = ViewData["ids"] as string[];
for (var i = 0; i < ids.Length; i++)
{
<option>#ids[i]</option>
}
}
</select>
<input name="text" type="submit" value="Submit" />
<button onclick="showValue()">Click me!</button>
}
</div>
</body>
</html>
So right now I have this alert function just to prove to myself that I have access to the option that I select. I'm pretty sure I need to use FormMethod.Post to get it back to the Controller, but I haven't been able to find any helpful references so far.
How do I format this so my variable text gets sent back into the controller?
I suggest to use jquery $.getJSON method to send and get variable from controller without refresh the page. I added .NetFiddle it works here
[HttpGet]
public JsonResult GetDropdownList()
{
var yourdata = db.uspGetAllEventIds().ToList();
Console.WriteLine(text);
return Json(new { data = yourdata}, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public ActionResult Dropdown()
{
// add your code here
}
//html
#using (Html.BeginForm("Dropdown","YourControllerName", FormMethod.Post))
{
<select id="EventId" name="eventId">
</select>
<input name="text" type="submit" value="Submit" />
}
<button style="margin-top:20px;" id="yourid">Fill Dropdown!</button>
// jquery
$("#yourid").on("click", function(){
showValue();
})
function showValue()
{
$.getJSON('#Url.Action("GetDropdownList", "YourControllerName")', function (result) {
$("#EventId").html(""); // makes select null before filling process
var data = result.data;
for (var i = 0; i < data.length; i++) {
$("#EventId").append("<option>"+ data[i] +"</option>")
console.log(data[i])
}
})
}
A better way of doing it is you do a postback to your Dropdown(...) action on button click and change its signature to Dropdown(string EventId) which should give you selected EventId.
Here is the full code using both the methods.
public class EventsController : Controller
{
public ActionResult Dropdown()
{
ViewData["ids"] = new[] { 1, 2, 3 };
return View();
}
[HttpPost]
public ActionResult Dropdown(string eventId)
{
//call your sp instead of going back to the same page
ViewData["ids"] = new[] { 1, 2, 3 };
return View();
}
public ActionResult Direct(int id)
{
//call your sp instead of going back to the same page
ViewData["ids"] = new[] { 1, 2, 3 };
return View("Dropdown");
}
}
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script>
function showValue() {
var list = document.getElementById("EventId");
var text = list.options[list.selectedIndex].text;
$.getJSON('/Events/Direct/' + text, function(data) {
console.log(data);
});
}
</script>
</head>
<body>
<div>
#using (Html.BeginForm("Dropdown", "Events", FormMethod.Post))
{
<select id="EventId" name="eventId">
#{
var ids = ViewData["ids"] as int[];
for (var i = 0; i < ids.Length; i++)
{
<option>#ids[i]</option>
}
}
</select>
<button type="submit">Postback</button>
}
<button onclick="showValue()">Get JSON</button>
</div>
</body>
</html>
So, just a little background ... I need to process data in my controller with data that is coming from the Client side. Thus, knockout.js was suggested.
This particular page has a main page, and then multiple placeholders which all have the same format. The main issue is that only one of these is appearing, and that happens to be the last one. When I inspect element, I see the data present, but not rendered.
here is the code:
First ... the markup for the parent item.
#using (Html.BeginFooterScripts())
{
<script type="text/javascript" src="//ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"></script>
<script type="text/javascript" src="/Content/Northwestern/js/_libs/knockout.mapping/knockout.mapping.2.4.1.js"></script>
<script type="text/javascript" src="~/Content/Northwestern/js/views/TabPanel/location-card.js"></script>
<script type="text/javascript">
var once = true;
$(function () {
if (once) {
initialize();
once = false;
}
});
</script>
}
<div class="resp-tabs-container tabs-narrow">
#if (Model.TabSelector.Equals("location-row"))
{
<div>
#*this div needs to be empty. The easyResponsiveTabs plugin adds classes to it and matches it with the <li> in the resp-tab-list above*#
#*Location List *#
<div class="#Model.TabSelector">
<div class="indent">
<h3>#Model.Title</h3>
</div>
#Html.Sitecore().DynamicPlaceholder("Location-Card")
</div>
</div>
}
The DynamicPlaceholder is where the problem is ... currently there are 9 identical Location-Cards
Here is the markup for the LocationCard.cshtml
#using (Html.BeginFooterScripts())
{
<script type="text/javascript" src="~/Content/Northwestern/js/views/TabPanel/location-card.js"></script>
<script>
$(function() {
geoLocate(function(location) {
var latitude = location.coords.latitude;
var longitude = location.coords.longitude;
displayLocation('#Model.LocationId', latitude, longitude);
});
});
</script>
}
<div id="detail-container">
</div>
<script type="text/html" id="location-detail-template">
<h2 class="location-title" itemprop="name" data-bind="text: ItemName">#Model.Location.ItemName</h2>
</div>
<div class="distance">
<i class="material-icons">place</i> <span data-bind="text: Distance.toFixed(1)"> Mi</span>
</div>
<div class="location-phone">
<a data-bind="attr: { 'href': clickToCallify(Phone), 'data-track-event': 'Find a Location - Detail', 'data-track-action': 'call icon' }" class="tel" itemprop="telephone">#Model.Location.Phone</a>
</div>
</div>
</div>
</script>
</div>
<div class="col lg-6 xl-7">
#(new HtmlString(Model.Body))
</div>
</div>
}
and here is the Location-card.js
var applied = false;
var geoLocateError = function onError(error) {
alert(error.message);
};
function ViewModel() {
var self = this;
self.currentLocation = {
latitude: 0,
longitude: 0
};
}
var viewModel = new ViewModel();
$(function () {
});
function initialize() {
ko.applyBindings(viewModel);
geoLocate(function(location) {
initLocation(location);
}, geoLocateError);
}
/**********************************************
* Location Functions
**********************************************/
function initLocation(location) {
viewModel.currentLocation = {
latitude: location.coords.latitude,
longitude: location.coords.longitude
};
}
function displayLocation(id, lat, lng) {
var apiUrl = '/api/northwestern/locations/getlocationbyid/' + id;
var data = {
'latitude': lat,
'longitude': lng
};
self.LocationId = id;
$.getJSON(apiUrl, data, function (response) {
var fragment = document.createDocumentFragment(),
container = document.createElement('div'),
viewModel = response;
fragment.appendChild(container);
// merge together all the display types into a commma-separated list
response.TypeListDisplay = $.map(response.Types, function (obj, t) {
return obj.ItemName;
}).join(', ');
ko.renderTemplate(
"location-detail-template",
viewModel, {
afterRender: function () {
$('#detail-container').html(container.innerHTML);
}
},
container
);
});
}
So, I am not certain what to do. I have been working on this now for several days
any help would be appreciated.
We have a view using Razor and Knockout.js that displays a form. Part of the form asks the user to enter a list of values, and we're using a ko.observablearray to keep track of them. This list is represented as a bunch of text boxes, one per value, with a "Delete" button next to each box and a single "Add" button underneath all of them. It works similarly to the demo project at http://learn.knockoutjs.com/#/?tutorial=collections.
Our form is acting unexpectedly in two ways:
When a delete button is clicked, it removes all values from the ko.observablearray, not just the one corresponding to what was clicked.
When the "Submit" button for the overall form is clicked, it adds a new element to the ko.observablearray instead of submitting the form to our server.
Why are we seeing this behavior? (I know that these are two separate issues, but I'm not sure if they're caused by the same underlying problem or not, which is why I'm posting them in one question.)
Here is our Razor view:
#model OurProject.Models.Input.InputModel
#{
ViewBag.Title = "Input";
}
<h2>Inputs</h2>
<div id="inputForm">
<!-- snip - lots of input elements to fill in that are bound to KO -->
<div>
#Html.LabelFor(model => model.POSTransactionCodes)
</div>
<div>
<span class="help-block">Separate values by commas.</span>
</div>
<div>
<ul data-bind="foreach: POSTransactionCodes">
<li><input data-bind="value: $data" /> Delete</li>
</ul>
<button data-bind="click: addPOSTransactionCode">Add another POS Transaction Code</button>
#Html.ValidationMessageFor(model => model.POSTransactionCodes, null, new { #class = "help-inline" })
</div>
<!-- snip - more input elements -->
<button data-bind="click: save">Submit</button>
</div>
<script type="text/javascript" src='~/Scripts/jquery-1.8.2.min.js'></script>
<script type="text/javascript" src='~/Scripts/knockout-2.1.0.js'></script>
<script type="text/javascript" src='~/Scripts/OP/OP.js'></script>
<script type="text/javascript" src='~/Scripts/OP/Input/OP.Input.Input.Form.js'></script>
<script type="text/javascript" src='~/Scripts/OP/Input/OP.Input.Input.Data.js'></script>
<script type="text/javascript">
var elementToBindTo = $("#inputForm")[0];
OP.Input.Input.Form.init(elementToBindTo);
</script>
Here is our main piece of Knockout code, OP.Input.Input.Form.js:
extend(OP, 'OP.Input.Input.Form');
OP.Input.Input.Form = function (jQuery) {
//The ViewModel for the page
var ViewModel = function () {
var self = this;
//Fields
/* snip - lots of ko.observables() */
self.POSTransactionCodes = ko.observableArray([]); //is a list of transaction codes
/* snip - lots of ko.observables() */
//Set up with initial data
self.initialize = function () {
var c = function (data, status, response) {
if (status === "success") {
/* snip - lots of ko.observables() */
ko.utils.arrayPushAll(self.POSTransactionCodes, data.POSTransactionCodes);
self.POSTransactionCodes.valueHasMutated();
/* snip - lots of ko.observables() */
} else {
}
};
OP.Input.Input.Data.GetInput(c);
}
//When saving, submit data to server
self.save = function (model) {
var c = function (data, status, response) {
if (status === "success") {
//After succesfully submitting input data, go to /Input/Submitted
//in order to let MVC determine where to send the user next
window.location.href = "~/Input/Submitted";
} else {
}
};
OP.Input.Input.Data.SaveInput(model, c);
}
//Modifying POSTransactionCodes array
self.removePOSTransactionCode = function (POScode) {
self.POSTransactionCodes.remove(POScode)
}
self.addPOSTransactionCode = function () {
self.POSTransactionCodes.push("");
}
};
//Connect KO form to HTML
return {
init: function (elToBind) {
var model = new ViewModel();
ko.applyBindings(model, elToBind);
model.initialize();
}
};
} ($);
Here is OP.Input.Input.Data.js:
extend(OP, 'OP.Input.Input.Data');
OP.Input.Input.Data = {
GetInput: function (callback) {
$.get("/API/Input/InputAPI/GetInputModel", callback);
},
SaveInput: function (input, callback) {
$.ajax({
url: "/API/Input/InputAPI/SaveInput",
type: "post",
data: input,
complete: callback
});
}
};
You need to be pushing a new ViewModel into your observable array. Which will contain observable properties.
So to do this I created a new view model called TransactionCodeView
var TransactionCodeView = function() {
var self = this;
self.code = ko.observable("");
};
Then when the user clicks "Add another POS Transaction Code":
self.addPOSTransactionCode = function () {
self.POSTransactionCodes.push(new TransactionCodeView());
}
The only other thing changed was in the HTML binding:
<li><input data-bind="value: code" /> Delete</li>
Because code is the observable property in the new viewmodel we bind the input value to that.
Take a look at this jsfiddle. I haven't tested the submit functionality for obvious reasons ;-)
This is why the submit functionality wasn't working on my form:
In the view, I had this Razor:
<div>
<ul data-bind="foreach: POSTransactionCodes">
<li><input data-bind="value: $data" /> Delete</li>
</ul>
<button data-bind="click: addPOSTransactionCode">Add another POS Transaction Code</button>
#Html.ValidationMessageFor(model => model.POSTransactionCodes, null, new { #class = "help-inline" })
</div>
Using the button element for my "Add" button was causing it to respond to the user pressing enter instead of the submit button at the end of the form. When I changed the button into an input element instead, it started working as expected.
<input type="button" value="Add another POS Transaction Code"
data-bind="click: addPOSTransactionCode" />