asp.net mvc spa durandaljs date format not working with Knockout - asp.net-mvc-4

I'm exploring durandaljs for asp.net mvc SPA. I'm using APS.net MVC4, Durandaljs, Knockoutjs, breeze, moment and other libs found under hottowel SPA sample.
I have a client view which is bound with DOB, DateTime.
<td colspan="2">
<span id="dob" data-bind="text: DOB"></span>
</td>
and my ViewModel contains code
vm.studentProfile().DOB(moment(vm.studentProfile().DOB()).format('L'));
logger.log(vm.studentProfile().DOB(), null, system.getModuleId(vm), false);
Above code actually comes from querySucceeded. i.e
return manager
.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
This supposed to be working as I've achieved this already for some other fields but in case of DateTime KnockoutOut doesn't update GUI whereas I can see the UPDATED format date in console log. Can somebody tell me what am I missing here. thanks in advance.

The problem may lie with the fact that DOB is a MomentJs date, not a JavaScript Date or string. You most likely need to add a custom binding handler for displaying these dates, such as for example:
ko.bindingHandlers.moment = {
update: function(element, valueAccessor) {
var value = valueAccessor();
var formattedValue = ko.utils.unwrapObservable(value).format('LLLL');
$(element).text(formattedValue);
}
};
Now, instead of using the "text" binding handler, use the "moment" binding handler like this:
<span id="dob" data-bind="moment: DOB"></span>
Edit: added an example of adding custom plugins using AMD modules with RequireJS:
require(['jquery', 'json2', 'sammy', 'amplify', 'bootstrap', 'moment', 'toastr', 'showdown', 'markdowneditor', 'spin'],
function($){
// Require that plugins be loaded, after the prerequisite libraries
// We load the plugins here and now so that we don't have to
// name them specifically in the modules that use them because
// we don't want those modules to know that they use plugins.
requirejs([
'jquery.ui', // jquery plugin
'jquery.mockjson', // jquery plugin
'jquery.tmpl', // jquery plugin
],
function () {
require(['ko'],
function(ko) {
// ensure KO is in the global namespace ('this')
if (!this.ko) {
this.ko = ko;
};
requirejs([
'libs/knockout.binding.handlers', // Knockout custom binding handlers
'libs/knockout.extenders', // Knockout custom binding handlers
'libs/bootstrap.extenders', // Knockout custom binding handlers
],
// Plugins generally don't return module objects
// so there would be point in passing parameters to the function
function () {
require(['app'], function(App) {
App.initialize();
});
}
);
}
);
}
);
}
);

What about just making a ko.Computed like the following
vm.studentProfileFormatted = ko.computed({
read: function () {
return moment(vm.studentProfile().DOB()).calendar();
},
write: function (value) {
var time = moment(value, "MM-DD-YYYY").toJSON();
vm.studentProfile(time);
},
owner: vm
});
And then calling studentProfileFormatted in your view.

Related

dojo JsonRest call not working

I'm trying to call my RESTful service from dojo. All I can see from debugger is, it tries to call the service but it doen't reach there. There are no errors. I can see the 'hello' alert.
define(["dojo/store/JsonRest","dojo/domReady!"],
function(JsonRest){
alert("Hello");
var rest = new JsonRest({
target: "/my/rest/call"
});
}
);
I's following this page from dojotoolkit.
But if i call using a declare then it works.
define(["dojo/store/JsonRest","dojo/_base/declare","dojo/domReady!"],
function(JsonRest, declare){
var rest = declare(JsonRest);
var restResult = new rest({
target: "/my/rest/call"
});
}
);
What am I doing wrong here?
error messages in console:
You're not following that tutorial to the letter. The difference is that you're using define and not require. Dojo's define is used in combination with declare to create new Dojo classes. Dojo's require is used to load and use existing classes. The link below is a recommended read and in your case pay special attention to the 'Requiring modules' and 'Defining modules' parts:
https://dojotoolkit.org/documentation/tutorials/1.8/modules/
If you use require like in that tutorial, it works perfectly:
require([
'dojo/store/JsonRest',
], function(
JsonRest
) {
new JsonRest({
target: 'some/resource/'
}).get(1).then(function (item) {
alert(JSON.stringify(item));
});
});
Here's a working example on Plunker: http://plnkr.co/edit/ZhsO67BFpWB5Txqy0Zl9?p=preview

SailsJs Handlebar helper functions does not work

I am using Handlebars as templating engine for Sailsjs. Basic templating is working fine but I can't find out the way to use Handlebars helper function or even built in functions are not available.
I have managed to solve the issue with partials using following article.
https://github.com/balderdashy/sails/issues/2414
I have also registered the helpers.js in config folder but I can't call any custom, built in blocks or iteration helper function.
Any pointers to solve the issue of helpers will be helpful.
Sailsjs verion - 0.11.4
Handlebars version - 4.0.5
I have registered the helper function in above file like this:
Handlebars.registerHelper('help', function() {
return "help22";
});
And I am calling the same in my template:
{{{help}}}
Any idea why it is not rendering?
OK, after few hours trying, I come up with a solution:
You can add this line to the end of config/helpers.js
module.exports = Handlebars.helpers;
Inside view.js:
module.exports.views = {
engine: 'handlebars',
layout: 'layout',
partials: 'partials',
helpers: require('./helpers')
};
It will work.
Above solution didn't work for me - I got error "Handlebars is not defined" because I didn't check this link- https://github.com/balderdashy/sails/issues/2414
I have had to add Handlebars = require('handlebars'); in /config/helpers.js
Putting all together:
Edit file /config/views.js
module.exports.views = {
engine: 'handlebars',
extension: 'html', // optional
layout: 'layouts/main', // optional, will load /views/layouts/main.html
partials: 'partials', // optional, will load partials from /views/partials/
helpers: require('./helpers') // <-- this is it
};
Create file /config/helpers.js
Handlebars = require('handlebars');
module.exports = Handlebars.helpers;
Handlebars.registerHelper('stringify', function(obj) {
var json = {}, prop, tmp;
for (prop in obj) {
if (obj.hasOwnProperty(prop)) {
try {
tmp = JSON.stringify(obj[prop]);
json[prop] = obj[prop];
} catch (e) {
json[prop] = '[CAN NOT stringify]';
}
}
}
return JSON.stringify(json, null, 2);
});
In template I use {{stringify entry}}
Tested on Sails v0.12.13

Knockout bindings getting lost in Durandal app

I have a SPA in which I am using Durandal / KnockoutJS with knockout.validation.js. Inside the {view}.js I have this set
ko.validation.configure ({
decorateElement: true,
registerExtenders: true,
messagesOnModified: true,
insertMessages: false,
messageTemplate: null,
parseInputAttributes: true,
});
and inside one of the views I have
<input class="input-member"
type="text"
data-bind="value: memberno, validationOptions: { errorElementClass: 'input-validation-error' }"/>
When the view is first activated the element correctly has the style input-validation-error applied.
On subsequent loading of the view the css is not applied as I require. I can see in firebug that the input field now has this class applied input-member validationElement
I don't know where validationElement came from - but something got messed up with the knockout bindings. I have tried moving the validation config into the shell.js but the result is the same (and not a good idea anyway).
Edit:
So far looks like errorElementClass: 'input-validation-error' is not being reapplied to the element after navigation. If the field is modified-focused-cleared , the validation fires normally. validationElement is the placeholder for the errorElementClass
Update
Found this bit of info at the github site and seems to be what im after
in {view}.js
function activate() {
vm.memberno.isModified(false);
return true;
}
The above code seems to work for input fields but not for select fields. Another idea I'm looking at is adding a binding handler like so
ko.bindingHandlers.validationElement = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var valueIsValid = valueAccessor().isValid();
if (!valueIsValid) {
$(element).addClass("input-validation-error");
} else {
$(element).removeClass("input-validation-error");
}
}
}
which works for all selects and inputs but is always on. If there's a way to deactivate this binding until the first form submit fires I think that will do it.
There needs to be a way to re-apply the binding or cause the binding to update. Try putting some code in the view model's activate function that forces validation.

fairytale about mvc, require.js and angular. is there happily ever after?

So. Once upon a time there were four magical creatures: asp.net mvc, require.js and angular. And one wise wizard decided to put them in the same house, and let for every single view of asp.net to have its own "code-behind" javascript file;
first he added to the _Layout.cshtml
<script data-main="/main" src="~/Scripts/require.js"></script>
and then he created main.js in the root:
require.config({
baseUrl: "/Scripts/",
paths: {
'jquery': 'jquery-1.9.1.min',
'jquery-ui': 'jquery-ui-1.10.2.custom.min',
'angular': 'angular.min',
'ng-grid': 'ng-grid-2.0.2.debug'
},
shim: {
'jquery': { exports: "$" },
'underscore': { exports: "_" },
'jquery-ui': ['jquery'],
},
});
// Standard Libs
require(['jquery','jquery-ui','underscore','angular']);
nothing fancy and magical yet. But then he created an html helper as such:
public static MvcHtmlString RequireJs(this HtmlHelper helper)
{
var controllerName = helper.ViewContext.RouteData.Values["Controller"].ToString(); // get the controllername
var viewName = Regex.Match((helper.ViewContext.View as RazorView).ViewPath, #"(?<=" + controllerName + #"\/)(.*)(?=\.cshtml)").Value; //get the ViewName - extract it from ViewPath by running regex - everything between controllerName +slash+.cshtml should be it;
// chek if file exists
var filename = helper.ViewContext.RequestContext.HttpContext.Request.MapPath("/Scripts/views/" + controllerName.ToLower() + "-" +
viewName.ToLower()+".js");
if (File.Exists(filename))
{
return helper.RequireJs(#"views/" + controllerName.ToLower() + "-" + viewName.ToLower());
}
return new MvcHtmlString("");
}
public static MvcHtmlString RequireJs(this HtmlHelper helper, string module)
{
var require = new StringBuilder();
require.AppendLine(" <script type=\"text/javascript\">");
require.AppendLine(" require(['Scripts/ngcommon'], function() {");
require.AppendLine(" require( [ \"" + module + "\"] );");
require.AppendLine(" });");
require.AppendLine(" </script>");
return new MvcHtmlString(require.ToString());
}
and then he could use it in _Layout.cshtml just like that:
#Html.RequireJs()
and if you were listening carefully to the story, you probably noticed that there was also Scripts/ngcommon.js file to manually bootstrap angular.js and have commonly used angular directives and services
require(['angular', 'jquery'], function() {
angular.module("common",[]).directive('blabla', function() {
return {
restrict: 'A',
scope: { value: "#blabla" },
link: function(scope, element, attrs) { }
}
});
//manually bootstrap it to html body
$(function(){
angular.bootstrap(document.getElementsByTagName('body'), ["common"]);
});
});
And here comes the magic: from now on if it was a javascript file in \Scripts\views named as controllerName-viewName.js as home-index.js for Home\Index.cshtml it would be automagically picked up by require.js and loaded. Beautiful isn't it?
But then the magician thought: What If I need to load something else (like ng-grid) and that something should not be injected into common angular module because not all the pages will be using it. Of course he could always manually bootstrap another module into a page element in each code-behind javascript where he needed, but he's not wise enough to find answer to the question:
Is it possible to inject some angular.js component (like ng-grid) directly into a controller, without having it as a part of the app module?
If I understand magician's idea right, then it is possible to go on by splitting your application into sub-modules being defined as a collection of components.
It will work if he sets up dependencies for main myApp module like:
var myApp = angular.module('myApp', ['Constants', 'Filters', 'Services', 'Directives', 'Controllers']);
myApp.Constants = angular.module('Constants', []);
myApp.Controllers = angular.module('Controllers', []);
myApp.Filters = angular.module('Filters', []);
myApp.Services = angular.module('Services', []);
myApp.Directives = angular.module('Directives', []);
Then each of sub-modules: Services etc. - can be extended with single component, like:
myApp.Controllers.controller('MyController', function () {});
myApp.Services.factory('myService', function () {});
myApp.Directives.directive('myDirective', function () {});
myApp.Filters.filter('myFilter', []);
myApp.Constants.constant('myConstant', []);
That way main application module is loaded with several sub-modules, but each structure is not important. It makes possible to include individual controllers, services, directives and filters on each page served from back-end - magician just needs to be sure that all needed dependencies are loaded.
DI is the magic key for having separate angular codebehind in MVC views.
You don't even need the requirejs at all, because angular is a dependency injector and module loader by nature, angular.bootstrap is the magic place to start.
So let wizard became more powerfull with the spell - $inject.
var TmplController = function($scope, $compile, $http... // any module itself
{
this.parts = ['legs','arms','head'];
$scope.dynamicPageTemplate = function($compile)
{
$compile('<div><p ng-repeat="each in parts">{{each}}</p></div>' )( $scope );
}
}
TmplController.$inject = ['$scope','$comple', '$http']; //try legs or head
refer complete annotated source of angular-scenario.js from https://github.com/angular/bower-angular-scenario, and you will find how to inject code with define manner helpers.

unobtrusive validation not working with dynamic content

I'm having problems trying to get the unobtrusive jquery validation to work with a partial view that is loaded dynamically through an AJAX call.
I've been spending days trying to get this code to work with no luck.
Here's the View:
#model MvcApplication2.Models.test
#using (Html.BeginForm())
{
#Html.ValidationSummary(true);
<div id="res"></div>
<input id="submit" type="submit" value="submit" />
}
The Partial View:
#model MvcApplication2.Models.test
#Html.TextAreaFor(m => m.MyProperty);
#Html.ValidationMessageFor(m => m.MyProperty);
<script type="text/javascript" >
$.validator.unobtrusive.parse(document);
</script>
The Model:
public class test
{
[Required(ErrorMessage= "required field")]
public int MyProperty { get; set; }
}
The Controller:
public ActionResult GetView()
{
return PartialView("Test");
}
and finally, the javascript:
$(doument).ready(function () {
$.ajax({
url: '/test/getview',
success: function (res) {
$("#res").html(res);
$.validator.unobtrusive.parse($("#res"));
}
});
$("#submit").click(function () {
if ($("form").valid()) {
alert('valid');
return true;
} else {
alert('not valid');
return false;
}
});
The validation does not work. Even if I don't fill any information in the texbox, the submit event shows the alert ('valid').
However, if instead of loading dynamically the view, I use #Html.Partial("test", Model) to render the partial View in the main View (and I don't do the AJAX call), then the validation works just fine.
This is probably because if I load the content dynamically, the controls don't exist in the DOM yet. But I do a call to $.validator.unobtrusive.parse($("#res")); which should be enough to let the validator about the newly loaded controls...
Can anyone help ?
If you try to parse a form that is already parsed it won't update
What you could do when you add dynamic element to the form is either
You could remove the form's validation and re validate it like this:
var form = $(formSelector)
.removeData("validator") /* added by the raw jquery.validate plugin */
.removeData("unobtrusiveValidation"); /* added by the jquery unobtrusive plugin*/
$.validator.unobtrusive.parse(form);
Access the form's unobtrusiveValidation data using the jquery data method:
$(form).data('unobtrusiveValidation')
then access the rules collection and add the new elements attributes (which is somewhat complicated).
You can also check out this article on Applying unobtrusive jquery validation to dynamic content in ASP.Net MVC for a plugin used for adding dynamic elements to a form. This plugin uses the 2nd solution.
As an addition to Nadeem Khedr's answer....
If you've loaded a form in to your DOM dynamically and then call
jQuery.validator.unobtrusive.parse(form);
(with the extra bits mentioned) and are then going to submit that form using ajax remember to call
$(form).valid()
which returns true or false (and runs the actual validation) before you submit your form.
Surprisingly, when I viewed this question, the official ASP.NET docs still did not have any info about the unobtrusive parse() method or how to use it with dynamic content. I took the liberty of creating an issue at the docs repo (referencing #Nadeem's original answer) and submitting a pull request to fix it. This information is now visible in the client side validation section of the model validation topic.
add this to your _Layout.cshtml
$(function () {
//parsing the unobtrusive attributes when we get content via ajax
$(document).ajaxComplete(function () {
$.validator.unobtrusive.parse(document);
});
});
test this:
if ($.validator.unobtrusive != undefined) {
$.validator.unobtrusive.parse("form");
}
I got struck in the same problem and nothing worked except this:
$(document).ready(function () {
rebindvalidators();
});
function rebindvalidators() {
var $form = $("#id-of-form");
$form.unbind();
$form.data("validator", null);
$.validator.unobtrusive.parse($form);
$form.validate($form.data("unobtrusiveValidation").options);
}
and add
// Check if the form is valid
var $form = $(this.form);
if (!$form.valid())
return;
where you are trying to save the form.
I was saving the form through Ajax call.
Hope this will help someone.
just copy this code again in end of modal code
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
;)