knockout template parsing of json data is not working - json

Please check this link is not working, i have no idea what is wrong in my code.
I am trying to create a blog application, which have title, description and comments, but i am not getting proper output.
<h4>Title</h4>
<label data-bind="value: title" />
<h4>Description</h4>
<label data-bind="value: description" />
<h4>Comments</h4>
<p data-bind="foreach: comments">
<label data-bind="value: commenter" /><br>
<label data-bind="value: comment" /><br>
</p>​
var data = {"title": "blog1",
"description": "Description1",
"comments": [{"commenter": "commenter1", "comment": "comment1"},
{"commenter": "commenter2", "comment": "comment2"},
{"commenter": "commenter3", "comment": "comment3"},
{"commenter": "commenter4", "comment": "comment4"}]};
function Comment(data) {
this.commenter = ko.observable(data.commenter);
this.comment = ko.observable(data.comment);
}
function BlogViewModel(data) {
var self = data;
self.title = data.title;
self.description = data.description;
self.comments = ko.observableArray(ko.utils.arrayMap(data.comments, function (com) {
return new Comment(com.commenter, com.comment);
}));
}
ko.applyBindings(new BlogViewModel(data));
​

You have multiple problems with your code some are related to KnockOut some of them are not:
Which are not related to KO:
In BlogViewModel the self variable should hold this not the data parameter: so it should be var self = this;
Your comment mapping is wrong: the new Comment(com.commenter, com.comment) should be new Comment(com)
Which are related to KO:
The value binding is used for input elements, you have labels so you need to use the text binding instead. E.g data-bind="text: title"
KO needs valid html. Because the self-closing label tag is not valid you need to add the closing tags to your labels e.g <label data-bind="text: description"></label>
Here is a working JSFiddle containg all the fixes.

Related

Saving and displaying "nested" JSON data from API

I have the following format of JSON data fetched from an API and stored in IntentList
{
"id": 22,
"name": "IntentName",
"fk_app": 3,
"fk_intent": null,
"nlu_models": [],
"sentences": [
{
"text": "text1",
"id": 2308
},
{
"text": "text2",
"id": 2309
},......
So there are these levels : the first having "name" and "sentences", and the second which is inside sentences, having "text".
My goal is to be able to search the API data By text and to display on each row the name and text found inside the sentences related to that text.
Therefore if I search "text" this would appear => IntentName text1 IntentName text2
if I search text1 this would appear => IntentName text1
-----What I have done/tried so far-----
I searched for corresponding text and stored its intent object and was only able to display its intent name
Typescript:
fetchIntentsTxt(event: any) {
if (event.target.value === '') {
return this.searchResultINTxt = [];
}
this.searchResultINTxt = this.IntentList.filter(
i => i.sentences.some(
s => s.text.toLowerCase().includes(event.target.value.toLowerCase())));
}
Html:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (keyup)="fetchIntentsTxt($event)"
class="form-control" id="search0" placeholder="Search for Intents by Text">
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt" >{{intentTxt.sentences.text(this doesn't work obviously)}}<span
class="float-right">{{intentTxt.name}}</span> <br></a>
Any recommendation is appreciated, even if it meant changing the search function. Thanks in advance.
Example: https://stackblitz.com/edit/angular-ivy-pyaq7a
I would just create a new array in the fetchIntentsTxt method and add the data I want to display by the user input/search. After "filtering" all the data I need I set it to the variable thats iterated over in the View/Template.
public fetchIntentsTxt(searchValue: string): void {
const searchResults = [];
for (const entry of this.intentList) {
for (const sentence of entry.sentences) {
if (sentence.text.toLowerCase().includes(searchValue)) {
searchResults.push({
name: entry.name,
text: sentence.text,
});
}
}
}
this.searchResultINTxt = searchResults;
}
View:
<input type="text" autocomplete="off" [(ngModel)]="searchInputINTxt" (ngModelChange)="fetchIntentsTxt($event)" class="form-control" id="search0" name="search0" placeholder="Search for Intents by Text"/><br />
<a #myIntentTxt *ngFor="let intentTxt of searchResultINTxt">
{{ intentTxt.text }}<span class="float-right">{{ intentTxt.name }}</span>
<br/>
</a>
Also note here, I used the (ngModelChange) instead of the (keyup) on the search input so I do not need to hassle around with the events and just get the value I need for filtering.

Convert html to checkbox razor view

I have the following checkbox:
<input type="checkbox" id="seaMode" name="transportMode" value="Origin Service" />
<label for="seaMode" data-label="Sea">Sea</label>
However I am using razor view for that binding the view to the model:
#foreach (var val in Model.TransportList)
{
#Html.Label(val.Value, htmlAttributes: new { #for = label })
<i class=#val.Image></i>
#Html.CheckBox(label, false, new { value = #val.Text, name = "transportMode" })
}
Right now, it is returning it as below:
<label for="SEAMode">SEA</label>
<i class="icon-sea"></i>
<input id="SEAMode" name="SEAMode" type="checkbox" value="1">
<input name="SEAMode" type="hidden" value="false">
Any one good with html can please help me to add the correct class and attribute so as to render the html as the 1st one?
For info, val.Value has SEA and val.Text = 1 and so on.
If you're trying to add a class and data attributes then this should do it:
#Html.CheckBox(label, false, new { value = #val.Text, name = "transportMode", #class = "myCheckBox", data_label = "my data label value" })
Let me know if this helps.

How to delete and update data in json format using read and write property of Node.js in angular2 application

I have a json file in assets/json/abc.json
I have a requirement that I need to read /abc.json File from assets folder and write some data or delete some data from that /abc.json file according to an input value from a form in html.
I have tried but its not working.
Here, read/write/delete .json file is according to an input from user through a click event.
abc.json
[
{
"imgPath": "fa-users",
"dashboardName": "Command Center",
"urlToVisit": "dashboards/static/commandcenter"
},
{
"imgPath": "fa-tachometer",
"dashboardName": "HP Dashboard",
"urlToVisit": "dashboards/static/hpdash"
},
{
"imgPath": "fa-cube",
"dashboardName": "HP APJ",
"urlToVisit": "dashboards/static/hpapj"
}
]
abc.component.html
<form class="rmpm" (submit)="AddNewDashboardBox($event)">
<div class="form-group rmpm">
<div class='col-xs-12 rmpm'>
Enter Dashboard Name
<br>
<input type="text" class="form-control rmpm" placeholder="Dashboard Name" name="dashboardName"
required>
<br>
</div>
<div class='col-xs-12 rmpm'>
Enter Icon Name
<br>
<input type="text"class="form-control rmpm" placeholder="Icon Name" name="IconName"
required>
<br>
</div>
<div class='col-xs-12 rmpm'>
Enter Url Path to Visit
<br>
<input type="text" class="form-control rmpm" placeholder="Url Path" name="UrlPath"
required>
<br>
</div>
</div>
<button type="submit" class="btn btn-info">Add</button>
</form>
abc.component.ts
AddNewDashboardBox(e) {
e.preventDefault();
let dashboardNameInput = e.target.elements[0].value;
let IconNameInput = e.target.elements[1].value;
let UrlPathInput = e.target.elements[2].value;
var obj = {
table: []
};
var json = JSON.stringify(obj);
var fs = require('fs');
fs.readFile('assets/json/abc.json', 'utf8', function readFileCallback(err, data) {
if (err) {
console.log(err);
} else {
obj = JSON.parse(data); //now it an object
obj.table.push({ "imgPath": IconNameInput , "dashboardName": dashboardNameInput, "urlToVisit": UrlPathInput }); //add some data
json = JSON.stringify(obj); //convert it back to json
fs.writeFile('assets/json/abc.json', json, 'utf8');
}
});
}
The angular application is running on client browser and the file you want to change is residing on server. So this thing is not possible.
You will have to write an api to which angular will make rest call with new data and then that server will make the required changes in file on file (that is on server).
I recommend you to see the client server architecture for in depth details.

Why are my inputs not resetting?

The below code generates several forms depending on data returned from the server. Everything generates fine, but after clicking on an AnswerOpenQuestion button the input does not clear/reset. What's going on here?
angularJs code:
var availableInterviewController = function($scope, $http) {
// define initial model
$scope.interviews = [];
// retrieve available interviews
$http.get('/api/UserInterviewsApi/AvailableInterviews')
.success(function(data) {
// update interviews
$scope.interviews = [];
$scope.interviews = data;
});
// define open question answer selection
$scope.Answer = "";
// define multiple choice selection
$scope.selectedChoice = "";
// define answer open question button
$scope.AnswerOpenQuestion = function() {
$scope.Answer = ans;
alert(q.Question + ' and ' + $scope.Answer);
$scope.Answer = ''; // <---This is not clearing/resetting the HTML form inputs
};
// define answer multiple choice button
$scope.AnswerMultipleChoice = function() {
//
};
};
// assign the new controller to the main angular app
myAngApp.controller('availableInterviewCtrl', availableInterviewController);
Html code:
<form class="form-group" ng-repeat="q in inter.Questions">
<fieldset style="display: inline-block;">
<legend>Question {{$index + 1}}</legend>
<!--Open Ended-->
<div class="form-group" ng-show="q.MultipleChoices.length === 0">
<label for="{{'quest-' + $index}}">
<strong class="text-info">{{q.Question}}</strong><br />
</label>
<input name="openQuestion" id="{{'quest-' + $index}}" type="text"
class="form-control" ng-model="Answer" />
<button ng-click="AnswerOpenQuestion()">Answer</button><br />
<span class="text-info">
asked by {{q.AskedByUserName ==
'Administrator' ? 'staff' : q.AskedByUserName}}
</span>
</div>
<!--Multiple Choice Question-->
<div class="form-group" ng-show="q.MultipleChoices.length > 0">
<label for="{{'quest-' + $index}}">
<strong class="text-info">{{q.Question}}</strong>
</label>
<div>
Select an answer:
<label ng-repeat="x in q.MultipleChoices">
<input name="currentChoice" type="radio" value="{{x.Id}}"
ng-model="selectedChoice" />
{{x.Choice}}
</label>
<button ng-click="AnswerMultipleChoice()">Answer</button><br />
<span class="text-info">
asked by {{q.AskedByUserName ==
'Administrator' ? 'staff' : q.AskedByUserName}}
</span>
</div>
</div>
</fieldset>
</form>
UPDATE - Solution
AngularJs:
// define open question answer selection
$scope.OpenAnswer = { Answer: '' };
// define answer open question button
$scope.AnswerOpenQuestion = function (q, ans) {
$scope.OpenAnswer.Answer = ans;
alert(q.Question + ' and ' + $scope.OpenAnswer.Answer);
// clear the input
$scope.OpenAnswer.Answer = '';
};
Html:
<input id="{{'quest-' + $index}}" type="text"
class="form-control" ng-model="OpenAnswer.Answer" />
Don't use the scope as a model instead make an object that wraps the data model and assign it to a property of the scope.
$scope.myModel = {Answer:''}
Also don't use value in most cases ngmodel is all you need for two way binding.
In js strings are immutable so the original reference is not being updated instead a new string is being made, the digest cycle won't see this as a change to the original string.

How do preserve leading and trailing whitespace when using an input tag?

I am playing with Angular and writing a Regex tester.
Problem is leading whitespace is trimmed when I enter data. See example jsfiddle here:
So when the page loads I have the RegEx "^\d+$".test(" 123 ") which results in "No Match", But if you enter an extra leading or trailing space in the Candidate box:
The leading and trailing spaces are removed from my variable
The result changes "Match"
Here is my HTML:
<div id='ng:app' class='ng-app: myApp' ng-app='myApp'>
<div ng-controller="Controller">{{addTodo2()}}
<form novalidate class="simple-form">Pattern:
<input type="text" ng-model="pattern" />Candidate:
<input type="text" ng-model="candidate" />
<br />.{{candidate}}.
<br>.{{candidate2}}.</form>
</div>
</div>
And here is the associated JavaScript:
function Controller($scope) {
$scope.pattern = "^\\d+$";
$scope.candidate = " 123 ";
$scope.candidate2 = " 123 ";
$scope.addTodo2 = function () {
var str = "Javascript is an interesting scripting language";
var re = new RegExp($scope.pattern, "g");
var result = re.test($scope.candidate);
if (result) {
return "Match22";
} else {
return "No Match22";
};
};
}
var myapp = angular.module('myApp', []);
Updated the fiddle, added ng-trim="false" to the input tags
http://jsfiddle.net/T2zuV/12/
<div id='ng:app' class='ng-app: myApp' ng-app='myApp'>
<div ng-controller="Controller">{{addTodo2()}}
<form novalidate class="simple-form">Pattern:
<input type="text" ng-model="pattern" ng-trim="false"/>Candidate:
<input type="text" ng-model="candidate" ng-trim="false"/>
<br />.{{candidate}}.
<br>.{{candidate2}}.</form>
</div>
</div>