Angular-Schema-Form Change FORM on SCHEMA change - javascript

I have got a problem with changing json schema with angular schema form.
If I set up schema like in code like this
$scope.schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Schema number ONE",
"type": "object",
"properties": {..
it works and renders whole form properly as I want.
But I want to load data from web service.
So I tried to set up schema to nothing and then change it by clicking button, but it didnt work. I mean, i got schema from service, but form do not change.
For example something like this in code.
$scope.schema = {};
$scope.changeSchema= function(){
$scope.schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": " Schema number two that I want",
"type": "object",
"properties": {
}
What I want is to select schema to load and change form to schema i selected.
Thank you very much.

As Claies pointed out in their comment, you need to trigger a schemaFormRedraw broadcast. However on load the error is due to validation on the schema that you have as {}, this would need to be a temporary schema, something along these lines should work:
$scope.schema = { "type": "object", "properties": {} }};
$scope.changeSchema = function() {
$scope.schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": " Schema number two that I want",
"type": "object",
"properties": {...}
}
$scope.$broadcast('schemaFormRedraw');
}

Related

Use variables in Azure Functions out binding

I'm using Azure functions with javascript, and i would like to modify the out binding of path in my functions. For example this is my function.json:
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
},
{
"name": "outputBlob",
"path": "container/{variableCreatedInFunction}-{rand-guid}",
"connection": "storagename_STORAGE",
"direction": "out",
"type": "blob"
}
]
I Would like to set {variableCreatedInFunction} in index.js, for example:
module.exports = async function (context, req) {
const data = req.body
const date = new Date().toISOString().slice(0, 10)
const variableCreatedInFunction = `dir/path/${date}`
if (data) {
var responseMessage = `Good`
var statusCode = 200
context.bindings.outputBlob = data
} else {
var responseMessage = `Bad`
var statusCode = 500
}
context.res = {
status: statusCode,
body: responseMessage
};
}
Couldn't find any way to this, is it possible?
Bindings are resolved before the function executes. You can use {DateTime} as a binding expression. It will by default be yyyy-MM-ddTHH-mm-ssZ. You can use {DateTime:yyyy} as well (and other formatting patterns, as needed).
Imperative bindings (which is what you want to achieve) is only available in C# and other .NET languages, the docs says:
Binding at runtime In C# and other .NET languages, you can use an
imperative binding pattern, as opposed to the declarative bindings in
function.json and attributes. Imperative binding is useful when
binding parameters need to be computed at runtime rather than design
time. To learn more, see the C# developer reference or the C# script developer reference.
MS might've added it to JS as well by now, since I'm pretty sure I read that exact section more than a year ago, but I can't find anything related to it. Maybe you can do some digging yourself.
If your request content is JSON, the alternative is to include the path in the request, e.g.:
{
"mypath":"a-path",
"data":"yourdata"
}
You'd then be able to do declarative binding like this:
{
"name": "outputBlob",
"path": "container/{mypath}-{rand-guid}",
"connection": "storagename_STORAGE",
"direction": "out",
"type": "blob"
}
In case you need the name/path to your Blob, you'd probably have to chain two functions together, where one acts as the entry point and path generator, while the other is handling the Blob (and of course the binding).
It would go something like this:
Declare 1st function with HttpTrigger and Queue (output).
Have the 1st function create your "random" path containing {date}-{guid}.
Insert a message into the Queue output with the content {"mypath":"2020-10-15-3f3ecf20-1177-4da9-8802-c7ad9ada9a33", "data":"some-data"} (replacing the date and guid with your own generated values, of course...)
Declare 2nd function with QueueTrigger and your Blob-needs, still binding the Blob path as before, but without {rand-guid}, just {mypath}.
The mypath is now used both for the blob output (declarative) and you have the information available from the queue message.
It is not possiable to set dynamic variable in .js and let the binding know.
The value need to be given in advance, but this way may achieve your requirement:
index.js
module.exports = async function (context, req) {
context.bindings.outputBlob = "This is a test.";
context.done();
context.res = {
body: 'Success.'
};
}
function.json
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
},
{
"name": "outputBlob",
"path": "test/{test}",
"connection": "str",
"direction": "out",
"type": "blob"
}
]
}
local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "node",
"str":"DefaultEndpointsProtocol=https;AccountName=0730bowmanwindow;AccountKey=xxxxxx;EndpointSuffix=core.windows.net"
}
}
Or you can just put the output logic in the body of function. Just use the javascript sdk.

Adaptive card(user input form) in BotFramework V4 nodejs gets reprompted after user submit

async feed_first(stepContext)
{
let company_card = MessageFactory.attachment(
CardFactory.adaptiveCard(testfeedback)
);
return await stepContext.prompt("textPrompt", {
prompt: company_card
});
}
async feed_second(stepContext) {
console.log("enter feedback second");
console.log(stepContext.context.activity.value);
}
{
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Container",
"items": [
{
"type": "Input.ChoiceSet",
"placeholder": "Placeholder text",
"choices": [
{
"title": " 1",
"value": " 1"
},
{
"title": " 2",
"value": " 2"
}
],
"style": "expanded",
"id": "cho"
},
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Submit",
"title": "Submit"
}
]
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
so for this code what happens is, the card gets displayed, but on the bot emulator when the submit button is clicked, nothing happens. the console displays " Running dialog with message activity" and the same card is prompted again. the bot doesn't flow to the second step(feed_second) of waterfall dialog. what i would like for the code to do is to display "enter feedback second" on the console and then show the selected choice with the id "cho" for stepContext.context.activity.value agiain on the console. side note- i have added "feed_first" and "feed_second" while declaring the WaterfallDialog, so that's not the the issue
If you want to use an Adaptive Card's inputs with a text prompt, you will need to access the activity's value and serialize it into the activity's text somehow, as seen in this example. This information is in the dialogs section of my Adaptive Cards blog post, which you should read.
In JavaScript you can serialize the value into text like this:
/**
*
* #param {TurnContext} turnContext
*/
async sendValueToDialogAsync(turnContext)
{
// Serialize value
var json = JSON.stringify(turnContext.activity.value);
// Assign the serialized value to the turn context's activity
turnContext.activity.text = json;
// Create a dialog context
var dc = await this.dialogs.createContext(turnContext);
// Continue the dialog with the modified activity
await dc.continueDialog();
}
In your case, if you only need the result of one input then you can just use that property instead of serializing anything:
turnContext.activity.text = turnContext.activity.value.cho;

Share generic logic and variable data across controllers

I have many controllers that operate within different pages of my application. I want to have alerts specific to each controller. An example for something pretty generic would be:
{
"saveSuccess": {
"alertClass": "success",
"header": "Success!",
"body": "File saved successfully.",
"buttons": [{
"label": "OK",
"tooltip": "close",
"fn": function () {
$scope.alertShown = false;
document.querySelector("#result").innerHTML = "success acknowledged";
}
}]
},
"confirmWarning": {
"alertClass": "warning",
"header": "Warning!",
"body": "This could cause a problem. Are you sure you want to proceed?",
"buttons": [{
"label": "OK",
"tooltip": "close",
"fn": function () {
$scope.alertShown = false;
document.querySelector("#result").innerHTML = "warning accepted";
}
}, {
"label": "CANCEL",
"tooltip": "close",
"fn": function () {
$scope.alertShown = false;
document.querySelector("#result").innerHTML = "warning canceled";
}
}]
},
"error": {
"alertClass": "critical",
"header": "Alert!",
"body": "The request cannot be processed. Please try again.",
"buttons": [{
"label": "OK",
"tooltip": "close",
"fn": function () {
$scope.alertShown = false;
document.querySelector("#result").innerHTML = "error acknowledged";
}
}]
}
}
But each page would have its own messages so this fairly generic data might get pretty length and pretty specific. I want to separate all this out into different files so I don't clutter all of my controllers with configuration objects like this. I could store it as stringified JSON and retrieve it using a service, then just repeat the fairly short logic that shows and hides the alert boxes between each controller, but stringifying functions seems kind of dumb and I feel there should be a fairly clean way to
a) make my template for the alert html a partial that can be rendered across the various views with something like <alert-html></alert-html>
b)somehow keep my model in a separate file that will be stored in each separate page's folder so there is a something like
-appPage1Dir
-appPage1View.html
-appPage1Controller.js
-appPage1AlertBoxModel.js
and I can put it into some var in appPage1Controller.js's scope
c)also pull in the show/hide logic into each page of the app.
I believe I could make a factory to return the data to my controller and pretty easily just inject the template into the view, but how can I elegantly also combine the show/hide logic so that's not repeated all over the place? I don't think that this is off-topic or opinion-based, I'm sure this is a common task in angular, but I'm just not familiar enough with it to know what to do.
JSBIN of the whole thing working in the controller.

AngularJS: How to create a model which holds an array for a dynamic list of input fields?

I have quite an interesting question (I hope) for all you AngularJS gurus out there. I am looking to create a dynamic list of form input fields based on a SELECT dropdown. As an example, we have a number of categories with each category having a set of specifications which are unique to that category. To help with the explanation we have the following:
Firstly, in the controller we start by initializing the models.
$scope.category = {};
$scope.category.specs = [];
Next we ready the data to be used in the form (actually retrieved from the server via $http). We also initialize a variable to the first element in the categories array.
$scope.categories = [
{ "id": "1", "name": "mobile", specs: [
{ "id": "1", "label": "Operating System" },
{ "id": "2", "label": "Camera type" } ] },
{ "id": "2", "name": "laptop", specs: [
{ "id": "1", "label": "Operating System" },
{ "id": "2", "label": "Graphics Card" } ] }
};
$scope.selectedCategory = $scope.categories[0];
In the form, we have a dropdown which when selected loads the appropriate input fields specific to that category. We use the ngRepeat directive to accomplish this. This is a dynamic list of fields based on $scope.categories.specs. (please note the ???)
<select ng-model="selectedCategory" ng-options="category.name for category in categories"></select>
<div ng-repeat="spec in selectedCategory.specs">
<label>{{spec.label}}</label>
<input type="text" ng-model="???">
</div>
Ultimately, when the user clicks the submit button, we would like to extract the category he/she has selected and then package it together with the specifications they have filled in. The post request should contain something like the following for instance (of course, I only included one spec item, but in reality there would be many):
{ "id": "1", specs [ { "id": "2", "details": "RADEON HD 8970M" } ] }
Unfortunately I am not really sure how to accomplish this. I need to somehow create an array for the spec model, and then ensure that both the ID and user entered data are appropriately extracted... what goes in the ??? and what do we do after? Any help would be much appreciated.
this is how I do it. I make a form, validate it with angular, and then when its valid I submit it with a function.
<form name="signup_form" novalidate ng-submit="signupForm()"></form>
$scope.signupForm = function() {
var data = $scope.signup;
$http({
method : 'POST',
url : 'http://yoursite.com/mail.php',
data : $.param(data), // pass in data as strings
headers : { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload)
})
.success(function(data) {
});
}
also if you want to look at another form validation system for angular check out http://nimbly.github.io/angular-formly/#!/ It may help you solve your current form system.
In the controller, initialize $scope.specDetails as follows:
$scope.specDetails = {};
angular.forEach($scope.categories, function (category, index1) {
$scope.specDetails[category.id] = {};
angular.forEach(category.specs, function (spec, index2) {
$scope.specDetails[category.id][spec.id] = '';
});
});
In the html, replace "???" with specDetails[selectedCategory.id][spec.id]

Reuse properties from a remote JSON schema, at the same level of the original schema

I have a remote schema "person.json", saved on another file.
{
"id":"#person",
"type":"object",
"properties": {
"name": {"type":"string"},
"gender": {
"type":"string",
"enum":["m", "f"]
},
"age": {"type":"number"}
},
"additionalProperties": false
}
And I have a "man.json" schema, being this one my original schema.
{
"id":"#man",
"type":"object",
"$ref":"person.json",
"properties": {
"beard":"boolean",
"moustache":"boolean"
},
"required": ["name"],
"additionalProperties": false
}
I want to use the properties: "name, gender, etc" from person.json at the same level as the properties: "beard, moustache" from man.json.
Example for validation
{
name: 'John',
gender: 'm',
age: 29,
beard: false,
moustache: true
}
I want to validate the previously shown example, as you see, with all the properties at the same level (not nested).
Is this possible? If yes, how? Thank you very much.
João
You want to use the allOf keyword, combined with $ref:
{
"id": "/schemas/man.json",
"allOf": [{"$ref": "person.json"}],
...
}
(Note: This is v4. In v3, the same thing was called extends.)
In plain English, it says "Every instance following Man schema must also follow the Person schema".
The bigger issue is in fact your use of additionalPropeties. Since person.json bans additional properties, any instance with a "beard" property is actually not a valid Person. If you're going to be extending Person, I advise you remove the constraint banning additional properties.
(Note: this behaviour is the subject of some conflict in the JSON Schema community, but this is what's in the specs.)
I'm guessing a piece of data cannot satisfy two schemas at once, so you'll need to create a third schema which combines them and validate against that.
var personSchema = JSON.parse(person_json);
var manSchema = JSON.parse(man_json)
for (var personProp in personSchema.properties) {
manSchema.properties[personProp] = personSchema.properties[personProp];
}
var manPersonSchema = JSON.stringify(manSchema);

Categories