Angular and Breeze edit Date object - javascript

I have an AngularJS application where I want to edit a Date object that is being exposed by ASP Web API through BreezeJS. When I try to edit this field in a HTML form it doesn't quite work. It seems to edit part of the object, or doesn't bind to the date input type at all.
I am wondering what the best way would be to edit Date fields in HTML forms with BreezeJS. I can't find any resources about how one would solve this problem in a proper way.
My own guesses would be:
Extend the Breeze entity with an extra field that is based on the original date with a getter/setter.
Not using a date field in the WebAPI, but that doesn't feel right.
Code
In my Angular controller I'm getting the object from Breeze through a repository pattern:
datacontext.session.getById(id)
.then(function (data) {
return vm.session = data;
});
After that the vm.session is filled with the Breeze object, like this:
[{
"$id":"1",
"$type":"TestApp.Model.Session, TestApp.Model",
"Id":3,
"Name":"Second session",
"StartDate":"2014-10-12T00:00:00.000",
}]
Now in my HTML form I'm binding like this:
<input type="date" ng-model="vm.session.startDate" placeholder="Start Date">
It's currenyly showing this date as "Sun Oct 12 2014 02:00:00 GMT+0200 (Romance Daylight Time)" if I use a type "text" instead of "date" in the input field.
And the casing of the ng-model field is correct since I'm using this to create camelCase fields:
breeze.NamingConvention.camelCase.setAsDefault();
So this is fairly straightforward... I'll try to make a JSFiddle or Plunker asap

Breeze maintains date properties as JavaScript Date objects when that's what you say they are in your metadata.
Breeze does not have its own Date type.
The HTML5 <input type="date" .../> date picker is not widely supported yet (see here) so I'm assuming you're relying on the Angular directive to help you.
That directive is designed to bind to a string, not a date object. This probably explains why the wheels are coming off.
I am sorely tempted to argue that the Ng directive should be smarter than this. It should do what it takes to bind to a Date object as well as a date string. I just might file that as a bug.
As you discovered, the HTML input tag "just works" if the type is "text". Obviously the date display and data entry are not what you want.
I think the ideal solution would be to have a directive that translates between date object and string value seamlessly. Probably could be done by decorating the existing Ng directive itself. Something to look into; we can always use your help; this is open source :-).
Meanwhile, you can create a defined property on your customSession type constructor.
See "Custom Constructors" in the documentation. Such a property would wrap the startDate property (startDateAsString?), parsing text in and formatting as string on the way out.
Here is a general purpose function to produce a Date wrapper property:
// Example: addDateWrapperProperty(SessionCtor, 'startDate', 'startDateAsString');
function addDateWrapperProperty(ctor, propName, wrapperPropName) {
Object.defineProperty(ctor.prototype, wrapperPropName, {
get: function () { return this[propName].toString(); },
set: function (value) { this[propName] = value; },
enumerable: true,
configurable: true
});
}
Use it to define your startDateAsString wrapper property and then bind to this property when editing (bind to the real property for readonly display).
Because you don't want Breeze to serialize this, add it to the ctor AFTER registering that ctor with Breeze metadata as shown below.
I don't know for certain that this will give you the behavior you're looking for. I'd need to see your jsFiddle or plunkr to be sure. But I think it will do the trick. Let us know!
I'm certainly not saying this is the ultimate answer. I'm not sure what that answer is ... although I'm thinking about a directive as I said.
Hope this gets you out of the jam for now. Sorry it's such a PITA. Who knew Ng wouldn't understand Date objects?
Example
Here's an example extracted from an actual Breeze test:
///// Test it //////
var OrderCtor = function () { }; // do-nothing custom constructor
// register the ctor FIRST, then add the property
manager.metadataStore.registerEntityTypeCtor('Order', OrderCtor);
addDateWrapperProperty(OrderCtor, 'orderDate', 'orderDateAsString');
// create new order with a test date
var testDate = new Date(2014,0,1,13,30); // 1/1/2014 1:30 pm
var order = em.createEntity('Order', {
companyName: "test cust",
orderDate: testDate
});
// confirm can read the date through the wrapper property
expect(order.orderDate.value).to.equal(testDate.value,
"'order.orderDate' should return " + testDate);
expect(order.orderDateAsString).to.equal(testDate.toString(),
"'order.orderDateAsString' should return " + testDate.toString());
var testDate2 = new Date();
// update orderDate by way of the wrapper property
order.orderDateAsString = testDate2.toString();
// confirm the update worked
expect(order.orderDate.value).to.equal(testDate2.value,
"after update, 'order.orderDate' should return " + testDate2);
expect(order.orderDateAsString).to.equal(testDate2.toString(),
"after update, 'order.orderDateAsString' should return " + testDate2.toString());
// confirm it is not a breeze-tracked property
var propInfo = order.entityType.getProperty('orderDateAsString');
expect(propInfo).to.equal(null, "'Order.orderDateAsString' is not known to metadata ");

Related

I override api_date of themplates in DHTMLX but it is not used?

In dhtmlx gantt, you can format input date (from string or any type) into javascript date. This date is used to draw chart. Base on the dhtmlx document, you can replace converter (api_date) by custom function:
I override the function as follow:
gantt.templates.api_date = function(date){
throw "It is called";
};
but, it never is called.
api_date template and config are no longer used. We will update the information in the documentation. Please use xml_date as hadi.mansouri suggested.
I don`t know why this function is not called. I also confuse for this, because official documents of DHMTLX Gantt (as you mentioned, api_date) say it should works.
However I found that if you override xml_date it will work as you want. Although it is named xml_date but it works for json data also.
So could use following snippet:
gantt.templates.xml_date = function(date){
// Your customized code.
// return a Date object.
};

How to detect changes with Date objects in Angular2?

Date objects that are modified using setDate method arent getting updated in template.
In template:
<p>{{date | date:'mediumDate'}}</p>
In component:
nextDay(){
this.date.setDate(this.date.getDate()+1);
}
But when I call nextDay function, the template isnt updated with the new value.
The only way I could get the change detection working was doing this:
nextDay(){
var tomorrow = new Date();
tomorrow.setDate(this.date.getDate()+1);
this.date = tomorrow;
}
Are there a better way to accomplish this same task?
I think that is the right way, to change the reference of the date variable. From the docs here we have:
The default change detection algorithm looks for differences by comparing bound-property values by reference across change detection runs.
So if the date reference remains the same, nothing will happen. You need a new Date reference and that's why the second version of nextDay() works.
If you remove the formatting pipe you will see that still only the second version of nextDay() works.

How to get internal value of Dojo DateTextBox widget?

I have a dijit/form/DateTextBox on a form, from which I need to get the internal hidden value (formatted as yyyy-MM-dd) and add that to the URL for an AJAX request.
How do I do this? Will any single method of DateTextBox give me that value?
My HTML for the field is:
<input id="ialFromDate">
I'm creating the widget with a fixed display format with this javascript:
var fDate1=new DateTextBox({constraints:{datePattern:'dd/MM/yyyy'}},'ialFromDate');
fDate1.startup();
If I use the browser UI to set the value to 1 September 2015, it displays as "01/09/2015", and there's a hidden input element with the value "2015-09-01", as expected.
I want to get the exact value of that hidden input element.
I've used the following code for testing:
var date1=fDate1.get('value');
console.log('date1='+date1);
And the console output is:
date1=Tue Sep 01 2015 00:00:00 GMT+1000 (AUS Eastern Standard Time)
This is not what I need, and not what I'd expect.
This seems to be the easiest way to get the date value in the desired format:
var date1=fDate1.valueNode.value;
It works, though I couldn't find any mention of valueNode in the Dojo documentation for dijit/form/DateTextBox.
Someone else posted this solution as either an answer or a comment to my question several hours ago, but that seems to have since been deleted.
You can try fDate1.valueNode.value. fDate1 is the DateTextBox Object which contains this hidden DOM Node(fDate1.valueNode). If you get the value of that hidden DOM Node you can have the string value you see in the DateTextBox.
The value you receive via get('value') is actually a full-blown Date object, which should effectively enable you to get whatever format you want, in various ways. For example:
var date1 = fDate1.get('value');
// stamp = dojo/date/stamp
console.log(stamp.toISOString(date1).slice(0, 10));

Breeze.js - Unable to resolve an expression for: 1367380800000 on entityType: Order:#Zza.Data

I am following the course on Pluralsight at Building Data-Centric Single Page Apps with Breeze. The code here was using 1.4.2 version of Breeze and was running on Web API v1 and EF 5. I upgraded the solution to EF 6, Web API v2 and Breeze v1.5.1 with some success. I was able to get a basic query up and running as well as a simple predicate running as shown below:
var productsWithBacon = function () {
var query = breeze.EntityQuery.from("Products").where("Description", "contains", "bacon");
app.em.executeQuery(query)
.then(function (data) {
products(data.results);
})
.fail(errorLoading);
};
The problem occurs when I try to do a complex predicate as seen here.
var ordersGreaterThan100InMay = function () {
clearCollections();
var gteMay1 = new breeze.Predicate("OrderDate", ">=", moment("2013-05-01"));
var lteMay31 = new breeze.Predicate("OrderDate", "<=", moment("2013-05-31"));
var gt100 = new breeze.Predicate("ItemsTotal", ">=", 100);
var predicate = breeze.Predicate.and(gteMay1, lteMay31, gt100);
var query = breeze.EntityQuery.from("Orders").where(predicate).expand("Customer");
app.em.executeQuery(query)
.then(function (data) {
orders(data.results);
})
.fail(errorLoading);
};
When executing, I get the error as seen in the title
Unable to resolve an expression for: 1367380800000 on entityType: Order:#Zza.Data
Originally, the code (as described in the course) was to use a line like:
var gt100 = new breeze.Predicate("ItemsTotal", "greaterThanOrEqual", 100);
This cause an error of:
Uncaught Error: Unable to resolve predicate after the phrase: 'ItemsTotal' for operator: 'greaterThanOrEqual' and value: '100'
but that was resolved by changing to ">=".
What is causing the expression error though?
The commenter in my post above was correct.
Apparently, either the update of Moment.js (Original version from course was 2.2.1) to the most recent version (2.8.2 as of this writing) may have changed something. I don't know if it was moment not returning an actual date object or something in Breeze that changed, but the following code does work.
var ordersGreaterThan100InMay = function () {
clearCollections();
var gteMay1 = breeze.Predicate.create("OrderDate", ">=", moment("2013-05-01").toDate());
var lteMay31 = breeze.Predicate.create("OrderDate", "<=", moment("2013-05-31").toDate());
var gt100 = breeze.Predicate.create("ItemsTotal", ">=", 100);
var predicate = breeze.Predicate.and(gteMay1, lteMay31, gt100);
var query = breeze.EntityQuery.from("Orders").where(predicate);
app.em.executeQuery(query)
.then(function (data) {
orders(data.results);
})
.fail(errorLoading);
};
Author of the course in question here. Not sure what to say since I agree with Ward - that has never been a supported operator as a string. It is the name of one of the FilterQueryOp enum values, so I must have made an error putting in that code where I copy/pasted the enum value name as a string instead of using it as an enumerated value. Embarrased that I shipped non-functional code in the course, will get that corrected next time I update the course.
I am perplexed by the code you're showing me. I see it, plain as day, at the 1'04" mark of the "Breeze Query Basics: Demo: Complex Filter Criteria" clip.
To my knowledge "greaterThanOrEqual" has never been a supported predicate comparison operator. I just searched the history of breeze on github back as far as I can go and I can't find it.
Similarly, AFAIK, a moment date has never been a valid predicate comparison value.
I'm flummoxed. I want to be wrong. I want to have reason to apologize for having broken your application with a change to breeze. I just can't find where this ever could have worked. I will ask the course author about how he made it work.
FWIW, you have figured out the proper syntax for today's breeze.
Update 13 December 2014
With help from course author, Brian Noyes, I found the regressions you discovered. Although both the use of FilterQueryOp symbol names (e.g., "greaterThanOrEqual") as operation names and the use of moment.js dates as predicate comparison values were never documented, they clearly did work back when the course was produced (around Breeze v.1.4.2). I decided we should support what Brian shows in his course and treat these as bona fide regressions. They will will be supported in the next breeze release (v.1.5.3?)
N.B.: Moment.js dates will only work as predicate comparison values. Moment.js is not yet supported as an alternate Date type anywhere else in Breeze. I'd like to see that happen but it isn't now and we have not scheduled time to add that support.

Formatting Date in Knockout Template

I'm wanting to format a date in knockout's template.
The date is currently being returned as
2013-07-04T00:00:00
I would like it to be displayed like
07/04/2013
Here is the binding I'm using
<td data-bind="text: FirstDate">
Are their default formatting properties in Knockout's template?
There is nothing built in regarding date formatting or formatting in general in Knockout. The text binding just converts the property value to string so if you want custom formatting you need to do it yourself.
Working with dates is not so easy in JavaScript so you are probably better with using a third party library like moment.js for this. It is very simple to use and it can format your dates with the format method. There is built in format 'L' for your required Month numeral, day of month, year formatting.
You can use moment js in your view-model or directly in your binding like:
<td data-bind="text: moment(FirstDate).format('L')">
Or you can create a custom binding handler which encapsulates this formatting logic.
Note: Make sure to use () on your FirstDate property if it is an ko.observable inside your data-binding expression to get its value.
I use moment.js in a modified version of Stephen Redd's date extender.
It looks like this, which is a little cleaner than calling a function in a data bind.
<input type="text" data-bind="value: dateOfBirth.formattedDate" />
You can also use MomentJs to create an extender:
ko.extenders.date = function (target, format) {
return ko.computed({
read: function () {
var value = target();
if (typeof value === "string") {
value = new Date(value);
}
return moment(value).format("LL");
},
write: target
});
}
viewmodel:
self.YourDate = ko.observable().extend({ date: true });
http://momentjs.com/docs/#/displaying/format/

Categories

Resources