Can't get multiple viewmodels under master viewmodel work in Durandal.JS - durandal

I am working on an application which has DurandalJS 2.0.1. I have written a viewmodel, basically I want to implement a master viewmodel that has multiple viewmodels (e.g. account viewmodel has register and login sub viewmodels)
define(['knockout'],function (ko) {
var register = function(){
var self = this;
self.Welcome = "Register";
self.Username = ko.observable();
self.Password = ko.observable();
}
var login= function(){
var self = this;
self.Welcome = "Login";
self.Username = ko.observable();
self.Password = ko.observable();
}
var account = {
testVariable : "Hello Cruel World!",
register : register,
login : login
};
return account;
});
View is :
<h2 data-bind="text: testVariable"></h2>
<h2 data-bind = "text: register().Welcome"></h2>
<h2 data-bind = "text: login().Welcome"></h2>
testVariable is being displayed correctly but I can't make register().Welcome, login().Welcome or tried register.Welcome or login.Welcome working.
Any idea how can I get it working?

Here's how you can tackle such situations... #robert.westerlund is right, if that is function then you've to create new object of each function before proceeding..
define(['plugins/http', 'durandal/app', 'jquery', 'knockout'], function (http, app, $, ko) {
var loginVM = {
Welcome : "Hello Login",
Username : ko.observable(),
Password : ko.observable(),
rememberMe : ko.observable(false),
Login : function (obj, event) {
//Do the login stuff
}
};
var registerVM = {
Welcome : "Hello Register",
Username : ko.observable(),
Password : ko.observable(),
ConfirmPassword : ko.observable(),
Register : function (obj, event) {
//Do the register stuff
}
};
return {
login : loginVM,
register: registerVM
};
});
VIEW
<div data-bind="with:login">
<h2 data-bind = "text: Welcome"></h2>
</div>
<div data-bind="with:register">
<h2 data-bind = "text: Welcome"></h2>
</div>
Instead of writing functions, I wrote object literals. That worked pretty well :-)

Related

How do I get the Selected Value of a DropDownList in ASP.NET Core MVC App

I have the following dropdownlist in my view:
#{
var listado = new List<SelectListItem>()
{
new SelectListItem()
{
Text ="YES",
Value ="1"
},
new SelectListItem()
{
Text = "NO",
Value = "2"
}
};
}
<div class="form-group">
<label class="control-label">Is it True ?</label>
#Html.DropDownList("miDropDownList",listado)
</div>
I want to get the selected value 'YES' or 'NO' to do something in the controller like so:
IActionResult ControllerAction(){
var theValue = dropdownList.SelectedValue //this is pseudocode syntax but you understand I want to get
//the value
// with the value I will do something like this:
User userinstance = new User {
Id = 1,
Name = "john",
isJohnTall = theValue.Value
}
}
I want something simple in other answers I've seen DropDownLists that are bound to models, but I just want to get strings selected in the dropdown and be able to do something with them in the controller.
You can use JQuery with ajax.
Something like this:
<div class="form-group">
<label class="control-label">Is it True ?</label>
#Html.DropDownList("miDropDownList", listado, new { #onchange = "GetYesOrNo()"})
</div>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
function GetYesOrNo() {
var selectElement = document.querySelector('#miDropDownList');
var option = selectElement.value;
$.ajax({
url: '/Home/GetYesOrNo',
type: 'GET',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
mimeType: 'text/html',
data: { getOption : option },
success: function (returnValue) {
alert(returnValue);
}
});
};
</script>
And in Home Controller, add this JsonResult:
public JsonResult GetYesOrNo(int getOption)
{
if (getOption == 1) return Json("Option: YES");
return Json("Option: NO");
}
You can use a form to submit your data.
The form will submit Value, so you can modify your value as Text.
You can change your code like below.
View:
#{
var listado = new List<SelectListItem>()
{
new SelectListItem()
{
Text ="YES",
Value ="YES"
},
new SelectListItem()
{
Text = "NO",
Value = "NO"
}
};
}
#using (Html.BeginForm("GetYesOrNo", "User"))
{
<div class="form-group">
<label class="control-label">Is it True ?</label>
#Html.DropDownList("miDropDownList", listado)
</div>
<button type="submit">Find</button>
}
Action:
public IActionResult GetYesOrNo(string miDropDownList)
{
//.....
return View();
}
Test result:
And how to bind to the model with dropdownlist,you can see my this reply,it may helpful.

Display Session timeout warning message before Session expires in ASP.NET Core

I can able to set the session end the below code.
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(2);
});
I need to extend the session after 20 minutes and if show the session time out warning message to the user and so the user can extend their time out from the application UI.
You already have session timeout code in your question. By the way, default value is 20 minutes. If you want more information, you can read more at Configuring Session.
As far as, I know ASP.NET doesn't have a build-in mechanism to display session expire notification message. So, we have to write our own.
Here is mine, and here is the usage. You are feel free to use it. Since I use Kendo UI, I use Kendo UI Window for the dialog. You could replace it with jQuery UI, if you do not want to use Kendo UI.
_SessionExpireNotification.cshtml
I keep the setting inside appsettings.json. You could hard coded them in this file.
#using Asp.Core
#using Microsoft.Extensions.Options
#using Asp.Web.Common
#inject IUserSession UserSession
#inject IOptions<AppSettings> AppSettings
#if (UserSession.IsAuthenticated)
{
#(Html.Kendo().Window()
.Name("SessionExpireNotification")
.Title("Need More Time?")
.Modal(true)
.Content(#<text>
<p>
Your session is about to expire. You will be automatically signed out in
</p>
<h2 style="margin-top: 0">
<span id="logout-counter-span">0#(AppSettings.Value.CookieAuthentication.SessionExpireNotificationMinutes):00</span>
</h2>
<p>
To continue your session, select <strong>Stay Signed In</strong>.
</p>
<p>
<button id="stay-logged-in-button" type="button" class="btn btn-primary">
Stay Signed In
</button>
<button id="signout-button" type="button" class="btn btn-default">
Sign out
</button>
</p>
</text>)
.Width(450)
.Visible(false)
.Events(ev => ev.Close("onSessionExpireNotificationClose"))
)
<script>
var notificationInterval,
logoutInterval,
logoutCounterSpan;
function startNotificationCounter() {
var counter = #AppSettings.Value.CookieAuthentication.ExpireMinutes;
notificationInterval = setInterval(function() {
counter--;
if (counter === #AppSettings.Value.CookieAuthentication.SessionExpireNotificationMinutes) {
$("#SessionExpireNotification").data("kendoWindow").center().open();
startLogoutCounter();
}
},
60000);
}
function startLogoutCounter() {
var counter = #(AppSettings.Value.CookieAuthentication.SessionExpireNotificationMinutes*60);
logoutInterval = setInterval(function() {
counter--;
if (counter < 0) {
$("#logoutForm").submit();
} else {
var m = Math.floor(counter / 60);
var s = Math.floor(counter % 60);
var mDisplay = m < 10 ? "0" + m : m;
var sDisplay = s < 10 ? "0" + s : s;
logoutCounterSpan.text(mDisplay + ":" + sDisplay);
}
},
1000);
}
function resetCounters() {
clearInterval(notificationInterval);
clearInterval(logoutInterval);
logoutCounterSpan.text("0#(AppSettings.Value.CookieAuthentication.SessionExpireNotificationMinutes):00");
startNotificationCounter();
}
function onSessionExpireNotificationClose() {
resetCounters();
}
$(function() {
logoutCounterSpan = $("#logout-counter-span");
startNotificationCounter();
$("#stay-logged-in-button").click(function() {
$.get("#Url.Action("Index", "KeepAlive", new {area = ""})",
null,
function(data) {
resetCounters();
$("#SessionExpireNotification").data("kendoWindow").center().close();
}
);
});
$("#signout-button").click(function() {
$("#logoutForm").submit();
});
});
</script>
}
Extending the session timeout is easy. You just call a dummy action method.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Asp.Web.Controllers
{
[AllowAnonymous]
public class KeepAliveController : Controller
{
//
// GET: /KeepAlive
[AllowAnonymous]
public ActionResult Index()
{
return Content("I am alive!");
}
}
}

Issue to display data from a module into another

I'm a beginner to MEAN.js and have an issue with displaying some datas from one module into another.
I've successfully created a new CRUD module "Jobs" which contains a list of jobs for my company.
I would like to associate a job from this list to a user (which is another crud module contained in the sample code)
When i'm editing a user on the admin panel i would like to be able to choose a job from a list and set it to the user in the database.
Here is what i've from now on :
user.server.model.js
var UserSchema = new Schema({
//username, firstname etc.
job: {
type: Schema.ObjectId,
ref: 'job'
}
});
job.server.model.js
var JobSchema = new Schema({
name:{
type: String
}
});
My html where i try to link them both :
edit-user.client.view.html
//Set a job for a user
<select ng-options="job.name for job in vm.jobs"></select> // <--- this doesn't work
Thank you for your help :) !
edit :
user.client.controller.js
angular.module('users.admin').controller('UserController', ['$scope', '$state', 'Authentication', 'userResolve',function ($scope, $state, Authentication, userResolve) {
$scope.authentication = Authentication;
$scope.user = userResolve; //...
job.client.controller.js
angular
.module('jobs')
.controller('JobsController', JobsController);
JobsController.$inject = ['$scope', '$state', 'Authentication', 'jobResolve'];
function RolesController ($scope, $state, Authentication, job) {
var vm = this;
vm.authentication = Authentication;
vm.job = job; //...
list-job.client.controller.js
angular
.module('jobs')
.controller('JobsListController', JobsListController);
JobsListController.$inject = ['JobsService'];
function JobsListController(JobsService) {
var vm = this;
vm.roles = JobsService.query();
}
Resolved, injecting my jobs service into my user controller did it:
angular.module('users.admin').controller('UserController', ['$scope', '$state', 'Authentication', 'JobsService', 'userResolve',
function ($scope, $state, Authentication, JobsService, userResolve) {
$scope.authentication = Authentication;
$scope.user = userResolve;
$scope.jobs = JobsService.query();
This have to be done in your server side endpoint.
Suppose you have a route like that:
router.post('/users/', function(req, res, next) {
var user = new User(req.body);
user.job = req.job;
user.save(function(err, user) {
if(err) { return next(err); }
res.json(user);
});
});

Pass ViewModel back to controller

I have a working application integrating Bootstrap and Knockout. This app pulls data from my controller and displays this in the UI as I would expect. I can see values are updated when I click or change a value but I can't seem to see that data passed back to my controller for the purposes of saving it. All I need to know is how to fix what I have to allow me to pass the selectedRequestorName back to the controller.
Here is a sample class
public class Requestor
{
public int Id { get; set; }
public string Name { get; set; }
}
Interface
interface IRequestorRepository
{
IList<Requestor> GetAllRequestors();
}
Here is the repository with the seed data
public class RequestorRepository : IRequestorRepository
{
private List<Requestor> requestors = new List<Requestor>();
private int _nextId = 1;
public RequestorRepository()
{
Add(new Requestor{ Id = 1, Name = "Brian" });
Add(new Requestor { Id = 2, Name = "Steve" });
Add(new Requestor { Id = 3, Name = "Jake" });
}
public IList<Requestor> GetAllRequestors()
{
return requestors;
}
public Requestor Add(Requestor item)
{
if (item == null)
{
throw new ArgumentNullException("Null Requestor");
}
item.Id = _nextId++;
requestors.Add(item);
return item;
}
}
My HomeController looks like the following
public class HomeController : Controller
{
static readonly IRequestorRepository req_repository = new RequestorRepository();
// GET: /Home/
public ActionResult Index()
{
ViewBag.DateNow = DateTime.Now.ToShortDateString();
return View();
}
public JsonResult GetRequestors()
{
return Json(req_repository.GetAllRequestors(), JsonRequestBehavior.AllowGet);
}
[HttpPost]
public JsonResult SaveDetails(Requestor selectedRequestorName)
{
int id = -1;
return Json(id, "json");
}
}
In my Index.cshtml I have the following in a script tag at the top of the page
// Global variable
var viewModel = null;
$(document).ready(function () {
function ViewModel() {
//Make the self as 'this' reference
var self = this;
// Requestors
self.RequestorId = ko.observable("");
self.RequestorName = ko.observable("");
self.RequestorSourceDatabase = ko.observable("");
var RequestorNames = {
Id: self.RequestorId,
Name: self.RequestorName,
SourceDatabase: self.RequestorSourceDatabase
};
self.selectedRequestorName = ko.observable();
self.RequestorNames = ko.observableArray(); // Contains the list of RequestorNames
// Initialize the view-model for Requestors
$.ajax({
url: '#Url.Action("GetRequestors", "Home")',
cache: false,
type: 'GET',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
self.RequestorNames(data);
}
});
// END Requestors
// Reset
self.reset = function () {
self.Name("");
}
// Cancel
self.cancel = function () {
self.Name(null);
}
}
viewModel = new ViewModel();
ko.applyBindings(viewModel);
});
$(function () {
$('#Save').click(function (e) {
// Check whether the form is valid. Note: Remove this check, if you are not using HTML5
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
type: "POST",
url: '#Url.Action("SaveDetails", "Home")',
data: ko.toJSON(viewModel.selectedRequestorName),
contentType: 'application/json; charset=utf-8',
async: true,
beforeSend: function () {
// Display loading image
},
success: function (result) {
if (result > 0) {
alert("This work request has been successfully saved in database. The Document ID is: " + result);
} else {
alert("The Work Request was not saved, there was an issue.");
}
},
complete: function () {
// Hide loading image.
},
error: function (jqXHR, textStatus, errorThrown) {
// Handle error.
}
});
}
else {
alert("Form is not valid");
}
});
});
And finally the control that contains the displayed data for the user to select from...
<p>Current selection is <span data-bind="text:selectedRequestorName"></span></p>
<!-- Requestors -->
<div class="input-group col-sm-8">
<input type="text" data-bind="value:selectedRequestorName" class="form-control item" placeholder="Requestor Name" name="Requestor">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle item" data-toggle="dropdown">Select <span class="caret"></span></button>
<ul class="dropdown-menu" data-bind="foreach: RequestorNames">
<li class="dropdown">
</li>
</ul>
</div>
</div>
<div>
<button id="Save" type="submit" class="btn btn-default btn-success">Create</button>
</div>
create an action method on your controller that accepts an selectedRequestorName (string?) as argument.
create a function in your knockout viewmodel that reads the selectedRequestorName from the ko vm, JsonStringify it and pass it back to the above action method via ajax.
[HttpPost]
public JsonResult SaveDetails(String selectedRequestorName)
{
int id = -1;
return Json(id, "json");
}
change type of selectedRequestorName to String from Requestor as above that should work.
note not tested.but let me know if it helps.
Within $('#Save').click(), would you please change the line
data: ko.toJSON(viewModel.selectedRequestorName)
with
data: ko.toJSON(viewModel.selectedRequestorName())
hope, this will help.

Pass selected item from drop down list to viewmodel

I have four controls on the page, a simple form with first and last names, date of birth and this drop down that contains some names of countries. When I make changes to the these controls I am able to see those changes in my viewModel that is passed in as a parameter in the SavePersonDetails POST below, but I never see the LocationId updated in that view model and I am not sure why.
This is what I have in my markup, Index.cshtml:
#model Mvc4withKnockoutJsWalkThrough.ViewModel.PersonViewModel
#using System.Globalization
#using Mvc4withKnockoutJsWalkThrough.Helper
#section styles{
#Styles.Render("~/Content/themes/base/css")
<link href="~/Content/Person.css" rel="stylesheet" />
}
#section scripts{
#Scripts.Render("~/bundles/jqueryui")
<script src="~/Scripts/knockout-2.1.0.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script src="~/Scripts/Application/Person.js"></script>
<script type="text/javascript">
Person.SaveUrl = '#Url.Action("SavePersonDetails", "Person")';
Person.ViewModel = ko.mapping.fromJS(#Html.Raw(Json.Encode(Model)));
var userObject = '#Html.Raw(Json.Encode(Model))';
var locationsArray = '#Html.Raw(Json.Encode(Model.Locations))';
var vm = {
user : ko.observable(userObject),
availableLocations: ko.observableArray(locationsArray)
};
ko.applyBindings(vm);
</script>
}
<form>
<p data-bind="with: user">
Your country:
<select data-bind="options: $root.availableLocations, optionsText: 'Text', optionsValue: 'Value', value: LocationID, optionsCaption: 'Choose...'">
</select>
</p>
</form>
This is my View Model:
public class PersonViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string LocationId { get; set; }
public IEnumerable<SelectListItem> Locations { get; set; }
}
I have a simple controller that loads my Person and a drop down list containing three countries.
private PersonViewModel _viewModel;
public ActionResult Index()
{
var locations = new[]
{
new SelectListItem { Value = "US", Text = "United States" },
new SelectListItem { Value = "CA", Text = "Canada" },
new SelectListItem { Value = "MX", Text = "Mexico" },
};
_viewModel = new PersonViewModel
{
Id = 1,
FirstName = "Test",
LastName = "Person",
DateOfBirth = new DateTime(2000, 11, 12),
LocationId = "", // I want this value to get SET when the user changes their selection in the page
Locations = locations
};
_viewModel.Locations = locations;
return View(_viewModel);
}
[HttpPost]
public JsonResult SavePersonDetails(PersonViewModel viewModel)
{
int id = -1;
SqlConnection myConnection = new SqlConnection("server=myMachine;Trusted_Connection=yes;database=test;connection timeout=30");
try
{
// omitted
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
myConnection.Close();
}
return Json(id, "json");
}
Lastly, here is my Person.js file, I am using knockout
var Person = {
PrepareKo: function () {
ko.bindingHandlers.date = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
element.onchange = function () {
var observable = valueAccessor();
observable(new Date(element.value));
}
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var observable = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(observable);
if ((typeof valueUnwrapped == 'string' || valueUnwrapped instanceof String) && valueUnwrapped.indexOf('/Date') === 0) {
var parsedDate = Person.ParseJsonDate(valueUnwrapped);
element.value = parsedDate.getMonth() + 1 + "/" + parsedDate.getDate() + "/" + parsedDate.getFullYear();
observable(parsedDate);
}
}
};
},
ParseJsonDate: function (jsonDate) {
return new Date(parseInt(jsonDate.substr(6)));
},
BindUIwithViewModel: function (viewModel) {
ko.applyBindings(viewModel);
},
EvaluateJqueryUI: function () {
$('.dateInput').datepicker();
},
RegisterUIEventHandlers: function () {
$('#Save').click(function (e) {
// Check whether the form is valid. Note: Remove this check, if you are not using HTML5
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
type: "POST",
url: Person.SaveUrl,
data: ko.toJSON(Person.ViewModel),
contentType: 'application/json',
async: true,
beforeSend: function () {
// Display loading image
},
success: function (result) {
// Handle the response here.
if (result > 0) {
alert("Saved");
} else {
alert("There was an issue");
}
},
complete: function () {
// Hide loading image.
},
error: function (jqXHR, textStatus, errorThrown) {
// Handle error.
}
});
}
});
},
};
$(document).ready(function () {
Person.PrepareKo();
Person.BindUIwithViewModel(Person.ViewModel);
Person.EvaluateJqueryUI();
Person.RegisterUIEventHandlers();
});
As you can see, I have the data in the page but none of them show as selected
Your solution is overly complex and is leading to certain weirdness with your data. Instead of trying to patch the Titanic, your best bet is to start over and simplify:
Your page's model contains all the information you need. There's no need to try to create two totally separate view models to work with the user data versus locations. With the mapping plugin, you can specify different "view models" for various objects in your main view model, and there's a simpler pattern that can be followed to set all that up. Here's what I would do:
// The following goes in external JS file
var PersonEditor = function () {
var _init = function (person) {
var viewModel = PersonEditor.PersonViewModel(person);
ko.applyBindings(viewModel);
_wireEvents(viewModel);
}
var _wireEvents = function (viewModel) {
// event handlers here
}
return {
Init: _init
}
}();
PersonEditor.PersonViewModel = function (person) {
var mapping = {
'Locations': {
create: function (options) {
return new PersonEditor.LocationViewModel(options.data)
}
}
}
var model = ko.mapping.fromJS(person, mapping);
// additional person logic and observables
return model;
}
PersonEditor.LocationViewModel = function (location) {
var model = ko.mapping.fromJS(location);
// additional location logic and observables
return model;
}
// the following is all you put on the page
<script src="/path/to/person-editor.js"></script>
<script>
$(document).ready(function () {
var person = #Html.Raw(#Json.Encode(Model))
PersonEditor.Init(person);
});
</script>
Then all you need to bind the select list to the locations array is:
<p>
Your country:
<select data-bind="options: Locations, optionsText: 'Text', optionsValue: 'Value', value: LocationId, optionsCaption: 'Choose...'">
</select>
</p>
Based on your updated question, do this.
1.We do not need locationsArray actually. Its already in user.Locations (silly me)
2.On Index.cshtml, page change the JavaScript like this.
var userObject = #Html.Raw(Json.Encode(Model)); // no need for the quotes here
jQuery(document).ready(function ($) {
Person.PrepareKo();
Person.EvaluateJqueryUI();
Person.RegisterUIEventHandlers();
Person.SaveUrl = "#Url.Action("SavePersonDetails", "Knockout")";
Person.ViewModel = {
user : ko.observable(userObject)
};
Person.BindUIwithViewModel(Person.ViewModel);
});
3.On your Person.js page, inside RegisterUIEventHandlers #Save button click event, do this.
$('#Save').click(function (e) {
var updatedUser = Person.ViewModel.user();
updatedUser.Locations.length = 0; // not mandatory, but we don't need to send extra payload back to server.
// Check whether the form is valid. Note: Remove this check, if you are not using HTML5
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
type: "POST",
url: Person.SaveUrl,
data: ko.toJSON(updatedUser), // Data Transfer Object
contentType: 'application/json',
beforeSend: function () {
// Display loading image
}
}).done(function(result) {
// Handle the response here.
if (result > 0) {
alert("Saved");
} else {
alert("There was an issue");
}
}).fail(function(jqXHR, textStatus, errorThrown) {
// Handle error.
}).always(function() {
// Hide loading image.
});
}
});
5.Finally, our markup
<p data-bind="with: user">
Your country:
<select data-bind="options: Locations,
optionsText: 'Text',
optionsValue: 'Value',
value: LocationId,
optionsCaption: 'Choose...'">
</select>
</p>
On an unrelated side-note,
The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callbacks are
deprecated as of jQuery 1.8. To prepare your code for their eventual
removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.