Clearing or resetting a knockout validation validatedObservable? - knockout-validation

I have a view model as such:
var prop1 = ko.observable().extend{ required: true },
prop2 = ko.observable().extend{ required: true };
var validation = ko.validatedObservable([prop1, prop2]);
function resetFields() {
prop1(undefined);
prop2(undefined);
}
var vm = {
prop1: prop1,
prop2: prop2,
validation: validation,
reset: resetFields
};
The properties prop1 and prop2 are being validated correctly via the validatedObservable, however when I execute resetFields, these properties then have errors on them since they've been modified and are required.
Is there a way to reset the validated observable, as if it had not been changed?
Update: I was sourcing knockout.validation from NuGet, and using v1.0.1

You cannot reset on the validatedObservable level but you can call clearError on the individual properties:
vm.reset();
console.log(vm.validation.isValid()); // output: false
prop1.clearError();
prop2.clearError();
console.log(vm.validation.isValid()); // output: true
Demo JSFiddle.
Note: It only works with a "recent" version of the validation plugin so the clearError is not included in the CDNJS and Nuget version of plugin. My JSFiddle demo uses the latest version (9fd5a4d2da) from GitHub.

When using a validated observable, I found out that you can call validatedObservable.errors.showAllMessages(false) after you have cleared the observable, which is behaving the way I was expecting in my application.

Related

Swagger: Add description with ref

I want to add a description to an object property that his definition is referenced. Something like that:
newCreditCard:
type: object
properties:
billingPhone:
description: Phone number of the card holder
$ref: "#/definitions/PhoneNumber"
But the editor warns that the description property will be skipped:
Extra JSON Reference properties will be ignored: description
I have found a less elegant workaround that works for the editor, but not for the Swagger UI (not sure that is may due to the recent update to 3.0.2 version of the Swagger UI)
newCreditCard:
type: object
properties:
billingPhone:
description: Phone number of the card holder
allOf:
- $ref: "#/definitions/PhoneNumber"
How do you do it in your Swaggers specification?
Thanks for the help!
If you add anything to the same level of $ref it will be ignored .
json $ref definition https://datatracker.ietf.org/doc/html/draft-pbryan-zyp-json-ref-03#section-3
correct way is to provide the description in the referenced object.
You could simply move the description property to the definition of PhoneNumber. Your original post does not show how you have defined PhoneNumber, but this snippet validates without warnings:
definitions:
phoneNumber:
type: string
description: Phone number of the card holder
newCreditCard:
type: object
properties:
billingPhone:
$ref: "#/definitions/phoneNumber"
If this answer is not what you are looking for, please restate the question. We need to know what you are trying to accomplish.
although it is not according to JSON standards.
if you are using Swashbuckle to generate your swagger.
i took advantage over the "Extensions" property of schema.
and managed to create a swagger JSON, with $ref and extended properties.
var refSchema = new OpenApiSchema
{
//Reference = new OpenApiReference { ExternalResource = referenceLink, Type = ReferenceType.Link }, this wont work and omit all your other properties
Extensions = new Dictionary<string, IOpenApiExtension>
{
{ "$ref" , new OpenApiString(referenceLink) } // adding ref as extension cause according to JSON standards $ref shouldnt have any other properties
},
Description = prop.Value.Description,
ReadOnly = prop.Value.ReadOnly,
Required = prop.Value.Required,
Type = prop.Value.Type,
Example = prop.Value.Example,
};
For anyone using Swashbuckle with ASP.NET, you can use the following code to have the $ref construct put under the allOf (just like the :
// do this wherever you are calling AddSwaggerGen()
ArgBuilder.Services.AddSwaggerGen(opts => {
opts.UseAllOfToExtendReferenceSchemas(); // add this line.
});
Now if you have a model with two properties of the same type, the individual descriptions for each field will show up in Swagger UI (e.g. below, both FooHeader and BarHeader are properties of type HttpHeader and their descriptions show up):

Breeze 1-To-Many Unidirectional Navigation Property not Populated

Breeze Version: 1.5.3
I'm experiencing something similiar to some older questions on SO but it seems like this "bug" is reoccurring:
I have a 1-To-Many unidirectional navigation property which is not populated. I have checked the metadata and the response from the server. I've even debugged into breeze and the node (or rawEntity) seems to be perfect.
I've tried to track it down and came to the conclusion, that it happens, because no "inverse"-Property is found for my Navigation Property and the mergeRelatedEntities-Function returning without updating the target Entity:
function mergeRelatedEntities(mc, navigationProperty, targetEntity, rawEntity) {
var relatedEntities = mergeRelatedEntitiesCore(mc, rawEntity, navigationProperty);
if (relatedEntities == null) return;
var inverseProperty = navigationProperty.inverse;
if (!inverseProperty) return;
var originalRelatedEntities = targetEntity.getProperty(navigationProperty.name);
originalRelatedEntities.wasLoaded = true;
relatedEntities.forEach(function (relatedEntity) {
if (typeof relatedEntity === 'function') {
mc.deferredFns.push(function () {
relatedEntity = relatedEntity();
updateRelatedEntityInCollection(relatedEntity, originalRelatedEntities, targetEntity, inverseProperty);
});
} else {
updateRelatedEntityInCollection(relatedEntity, originalRelatedEntities, targetEntity, inverseProperty);
}
});
}
Older Posts:
Non scalar navigation properties are not populating with "nodb" conception
and
Breeze (1.4.5) unidirectional one-to-many: navigation collection not populated
Edited 11. May 2015
Okay I start to understand what Ward meant with the unmapped properties (by finding a similar question from 2 years ago: Handling calculated properties with breezejs and web api)
What I have so far:
function iUIConfigConstructorTool() {
this.ConfigToCurrentUSetting = null;
};
function iUIConfigConstructorAppl() {
this.ConfigToCurrentUSetting = null;
};
function iUIConfigConstructorWidget() {
this.ConfigToCurrentUSetting = null;
};
function iUIConfigInitializer(uiConfigObject) {
// initializing other properties
};
this.manager.metadataStore.registerEntityTypeCtor("Tool", iUIConfigConstructorTool, iUIConfigInitializer);
this.manager.metadataStore.registerEntityTypeCtor("Appl", iUIConfigConstructorAppl, iUIConfigInitializer);
this.manager.metadataStore.registerEntityTypeCtor("Widget", iUIConfigConstructorWidget, iUIConfigInitializer);
This does what I want. Is there a way, to do this over the metamodel from the server? Because I define my calculated properties on the server and the metamodel is delivered by the server, I don't want to change the client-side implementation if I add a new Navigation-Property. So I'd need something like a flag in the metamodel to tell breeze, that this property needs to be filled as it comes over the wire without ForeignKeys etc.
Maybe in other words: We are doing "sub queries" on the server side (e.g. find Customers with it's Orders but only up to a certain Date) for each queried object and deliver this to breeze (in a separate property than the real orders-property of the Customer). Our problem is: How do we unpack this sub-query because there is no direct connection in metadata but we need the connection for the logic.
Please update your question with:
how you obtained/created metadata
metadata for the two endpoints (just the nav props, pk prop, and fk props will do)
the exact query expression
Of course a repro in plunker would be most welcome.
update 7 May 2015
If I understand your comment correctly, the navigation property (properties) in question are to be maintained by you (w/ server-supplied info), not by Breeze.
That leads me to suggest that you maintain them as unmapped properties rather than mapped, navigation properties. Does that make sense?

Possible bug in getValidationMessages

I've created a custom entity level validation function, very similar to the one in the documentation (http://www.breezejs.com/documentation/validation). When I call getValidationErrors(), I get the following error:
Cannot read property 'name' of undefined
The error is coming from:
proto.getValidationErrors = function (property) {
assertParam(property, "property").isOptional().isEntityProperty().or().isString().check();
var result = __getOwnPropertyValues(this._validationErrors);
if (property) {
var propertyName = typeof (property) === 'string' ? property : property.name;
result = result.filter(function (ve) {
**return (ve.property.name === propertyName);**
});
}
return result;
};
There is no 'property' field in the custom entity level validator context. I'm using Breeze 1.4.5. Is this a bug? It seems to me the code above should check for 've.property', before trying to access the name.
Update
This has been fixed as of Breeze 1.4.7, available now.
Previous Post:
This is a bug that has already been fixed on GitHub and will be part of the next release (Breeze 1.4.7) out sometime next week. Or you can pull the breeze.xxx.js file from GitHub now if you need the fix earlier.

breezejs: how to access enum metadata

I've noticed that in the metadata there's an object entityType but also an object enumType.
We use manager.metadataStore.getEntityType() to access the metadata of an Entity.
How can we do it for a given enum ? How would I create the enum on the client side out of the metadata ?
Also, when I assign an enum value to a property, I'd like to to it by name instead of by value.
For instance, assuming that Status is of type myEnum:
myEntity.Status = myEnum.Valid;
instead of
myEntity.Status = 1;
Does breeze have any helper function to access the values of an enum ?
This issue is still open as I write. But you might want to take a look at the work-around described in the answer to this SO question.
I am assuming that you are talking about data properties that are defined as .NET enums on the server, and you want additional metadata about these properties to be made available on the Breeze client.
Unfortunately, Breeze does not yet support any metadata on enum types other than the name of the .NET type backing the enum value. This is the 'enumType' property that will appear on any dataProperty that is backed by an .NET Enum on the server. (We do need to document this better)
Please add a feature request for this to the Breeze User Voice. It's a good idea and we do take these suggestions very seriously.
Well this is not exact solution to your question but definitely can help people who are generating metadata offline.
I am using NancyFx(No EF) + Breeze + AngularJS for my web project and generating breeze metadata offline(using EF methods at development) and then using it in js file.
I also encountered similar situation where I want to get all Enum values to bind dropdowns and to display EnumName corresponding to EnumValue(Id). I searched over net but there was not much as per my scenario.
So I have written raw JS methods
1. To extract all enums and their values(Id & Name) in a JS dictionary(associated array) from metadata.
var enumDictionary = {};
JSON.parse(window.app.metadata).schema.enumType.forEach(function (enumType) {
var newEnumValues = [];
enumType.member.forEach(function (enumValue) {
var newEnumValue = { id: enumValue.value, name: enumValue.name };
newEnumValues.push(newEnumValue);
});
enumDictionary[enumType.name] = newEnumValues;
});
I created a method to get all enum values for a specific enum. This will be used for binding a dropdown.
function GetEnumDictionary(enumName) {
return enumDictionary[enumName];
}
Another method I created to get specific Enum name on basis of value.
function GetEnumDictionaryValue(enumName, enumValueId) {
var result = null;
enumDictionary[enumName].some(function (enumValue) {
if (enumValue.id == enumValueId) {
result = enumValue.name;
return;
}
});
return result;
}

While Adding New Entity, Identity value is not generated

I am running into problem, where i extend the Entity to expose hasValidationError. Without that it works fine. Also i found that if I supply the ID before adding the entity it works fine as well. Why is the ID field not auto generating once the entity is extended on the client.
I am now using little different version of the code(i find it more intuitive to extend the entity this way), but it still errors out in the same way.
var Country = function () {
console.log("Country initialized");
var self = this;
self.Country_ID = ko.observable("");
self.Country_Code = ko.observable("");
self.Country_Name = ko.observable().extend({
validation: {
validator: function (val, someOtherVal) {
return false;//val === someOtherVal;
},
message: 'Invalid Value!',
params: 5
}
});
var prop = ko.observable(false);
var onChange = function () {
var hasError = self.entityAspect.getValidationErrors().length > 0;
if (prop() === hasError) {
// collection changed even though entity net error state is unchanged
prop.valueHasMutated(); // force notification
} else {
prop(hasError); // change the value and notify
}
};
// observable property is wired up; now add it to the entity
self.hasValidationErrors = prop;
//dummy property to wireup event
//should not be used for any other purpose
self.hasError = ko.computed(
{
read: function () {
self.entityAspect // ... and when errors collection changes
.validationErrorsChanged.subscribe(onChange);
},
// required because entityAspect property will not be available till Query
// return some data
deferEvaluation: true
});
self.fullName = ko.computed(
function () {
return self.Country_Code() + " --- " + self.Country_Name();
});
};
store.registerEntityTypeCtor("Country", Country);
and then in the button click i am using the following code to create new entity.
var countryType = manager.metadataStore.getEntityType("Country");
var newCountry = countryType.createEntity();
//newCountry.Country_ID(200); //if i add this line no errors occurs
newCountry.Country_Code("India");
self.list.push(newCountry);
manager.addEntity(newCountry); // validation error occurs right after this line
self.selectedItem(newCountry);
self.list.valueHasMutated();
Entities will only get their own autogenerated key if the metadata for their type specifies that this is supported. i.e.
if (myEntityType.autoGeneratedKeyType === AutoGeneratedKeyType.Identity)
This setting means that the key property of the entity is automatically generated by the server, typically for an 'Identity' column on your database.
or
if (myEntityType.autoGeneratedKeyType === AutoGeneratedKeyType.KeyGenerated)
This setting means that you have a server side KeyGenerator that can generate the key for you.
By default, however, myEntityType.autoGeneratedKeyType will equal AutoGeneratedKeyType.None.
In either of the other two cases, breeze will generate a temporary key on the client and then fix it up after a save completes with a 'real' key generated on the server.
If you do not need this capability, then simply create your own ctor for your type that generates a unique key and set it there. see MetadataStore.registerEntityTypeCtor for more details on how to register your ctor.
We are planning on improving our documentation in this area but haven't yet gotten there. I hope this helps.
Perhaps there is nothing wrong at all. How do you know that the id generation is failing? Is it a negative number after adding the newCountry to the manager? It should be.
What is the validation error that you are getting? Does it relate to Country_ID? Perhaps you have a validation constraint (e.g., minimum value) on Country_ID?
The addhasValidationErrorsProperty entity initializer works as intended. I just added a teaching test to the DocCode sample (see "Can create employee after registering addhasValidationErrorsProperty initializer" in entityExtensionTests.js). We haven't deployed it as I write this but you can get it from GitHub.
It follows your example as best I can with the Northwind Employee entity which has an identity id (Employee_ID). The test shows adding the initializer that I wrote in the previous post (not as you may have rewritten it). It shows that the new Employee's id is zero before adding to the manager and becomes -1 after adding to the manager. The -1 is the temporary id of the new employee; it receives a permanent value after save.
The default validationOptions of an EntityManager are set so to validate an entity when it is attached (or added) to the manager. You can change that behavior to suit your needs.
The new employee is in an invalid state when created; it lacks the required First and Last name values (the test displays these errors). Therefore the hasValidationErrors observable becomes true after adding the new employee to the manager and the hasValidationErrors observable raises a change notification that a Knockout UI would hear; these test shows these two points.

Resources