I'm still in process learning Angular.
For my current project I need to display HTML (eg: <h1>note1</h1><span>my first note</span><br><h1>note2</h1><span>my second note</span>) in my view. I know everyone will suggest to use ng-bind-html, but that is not what I want.
What I want is, i want to do checking if string HTML have note2 then display my second note.
In controller I know how to find string note2 which is use .match.
JSON
items =
[
{
"description": "<h1>note1</h1><span>my first note</span><br><h1>note2</h1><span>my second note</span>"
}
]
In controller find String note2
angular.forEach(items, function(value, key) {
if (value.description.match("note2")) {
console.log(value.description);
// will output HTML <h1>note1</h1><span>my...
}
else {
console.log("string not found");
}
});
I can do till find String note2, but to display my second note I don't have any idea.
Can someone help me with this,
thanks..
I haven't test this, but maybe you can use split function
angular.forEach(items, function(value, key){
if(items.description.match("note2")){
console.log(items.description);
var noteValues = items.description.split("<h1>note2</h1>");
$scope.mySecondNote = noteValues[1]; // value is <span>my second note</span>
}else{
console.log("string not found");
}
});
EDIT
Plunker DEMO here
It should be value.description., because items is an array
angular.forEach(items, function(value, key) {
if (value.description.match("note2")) {
}
DEMO
var app = angular.module('todoApp', []);
app.controller("dobController", ["$scope",
function($scope) {
items = [{
"description": "<h1>note1</h1><span>my first note</span><br><h1>note2</h1><span>my second note</span>"
}];
angular.forEach(items, function(value, key) {
if (value.description.match("note2")) {
console.log(value.description);
// will output HTML <h1>note1</h1><span>my...
} else {
console.log("string not found");
}
});
}
]);
<!DOCTYPE html>
<html ng-app="todoApp">
<head>
<title>To Do List</title>
<link href="skeleton.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<script src="MainViewController.js"></script>
</head>
<body ng-controller="dobController">
</body>
</html>
Change to
if (items[0].description.match("note2")) {
console.log(items[0].description);
// will output HTML <h1>note1</h1><span>my...
}
else {
console.log("string not found");
}
Related
https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example:htmlelement
In the specification they've provided an example for Creating an autonomous custom element. However, they've left _updateRendering() method implementation for the readers.
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static observedAttributes = ["country"];
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
// Left as an exercise for the reader. But, you'll probably want to
// check this.ownerDocument.defaultView to see if we've been
// inserted into a document with a browsing context, and avoid
// doing any work if not.
}
}
An issue has been raised to provide the remaining implementation for better understanding of the topic and quickly move on.
Issue: https://github.com/whatwg/html/issues/3029
What code can we put there to get the required functionality?
Your solution fails, once the flag is set you can never change the value.
That "exercise" is old.. very old.. and contrived to show everything Custom Elements can do.
And it is plain wrong.. key is WHEN attributeChanged runs, and what the old/new values are
And attributeChangedCallback runs BEFORE connectedCallback; that is why https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected was added.
Your code gets 3 parameters in attributeChangedCallback, but you can't do anything with them, because execution always goes to the _updateRendering method.
If the point of the exercise is to learn when Observed attributes change I would use:
Code also available in JSFiddle: https://jsfiddle.net/dannye/43ud1wvn/
<script>
class FlagIcon extends HTMLElement {
static observedAttributes = ["country"];
log(...args) {
document.body.appendChild(document.createElement("div"))
.innerHTML = `${this.id} - ${args.join` `}`;
}
attributeChangedCallback(name, oldValue, newValue) {
this.log("<b>attributeChangedCallback:</b>", `("${name}" , "${oldValue}", "${newValue}" )`);
if (this.isConnected) {
if (newValue == oldValue) this.log(`Don't call SETTER ${name} again!`);
else this[name] = newValue; // call SETTER
} else this.log("is not a DOM element yet!!!");
}
connectedCallback() {
this.log("<b>connectedCallback</b>, this.img:", this.img || "not defined");
this.img = document.createElement("img");
this.append(this.img); // append isn't available in IE11
this.country = this.getAttribute("country") || "EmptyCountry";
}
get country() { // the Attribute is the truth, no need for private variables
return this.getAttribute("country");
}
set country(v) {
this.log("SETTER country:", v);
// Properties and Attributes are in sync,
// but setAttribute will trigger attributeChanged one more time!
this.setAttribute("country", v);
if (this.img) this.img.src = `//flagcdn.com/20x15/${v}.png`;
else this.log("can't set country", v);
}
}
customElements.define("flag-icon", FlagIcon);
document.body.onclick = () => {
flag1.country = "nl";
flag2.setAttribute("country", "nl");
}
</script>
<flag-icon id="flag1" country="in"></flag-icon><br>
<flag-icon id="flag2" country="us"></flag-icon><br>
This is just one way, it all depends on what/when/how your Custom Elements needs to do updates.
It also matters WHEN the CustomElement is defined; before or after the DOM is parsed. Most developers just whack deferred or method on their scripts, without understanding what it implies.
Always test your Web Component with code that defines the Custom Element BEFORE it is used in the DOM.
A Real World <flag-icon> Web Component
Would be optimized:
<script>
customElements.define("flag-icon", class extends HTMLElement {
static observedAttributes = ["country"];
attributeChangedCallback() {
this.isConnected && this.connectedCallback();
}
connectedCallback() {
this.img = this.img || this.appendChild(document.createElement("img"));
this.img.src = `//flagcdn.com/120x90/${this.country}.png`;
}
get country() {
return this.getAttribute("country") || console.error("Missing country attribute",this);
}
set country(v) {
this.setAttribute("country", v);
}
});
</script>
<flag-icon id="flag1" country="gb"></flag-icon>
<flag-icon id="flag2" country="eu"></flag-icon>
Or NO External Images at all
Using the FlagMeister Web Component which creates all SVG client-side
<script src="//flagmeister.github.io/elements.flagmeister.min.js"></script>
<div style="display:grid;grid-template-columns: repeat(3,1fr);gap:1em">
<flag-jollyroger></flag-jollyroger>
<flag-un></flag-un>
<flag-lgbt></flag-lgbt>
</div>
Here is the complete code to achieve the same requirements:
<!DOCTYPE html>
<html lang="en">
<body>
<flag-icon country="in"></flag-icon><br>
<flag-icon country="nl"></flag-icon><br>
<flag-icon country="us"></flag-icon><br>
</body>
<script>
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static observedAttributes = ["country"];
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
//**remaining code**
if (this.ownerDocument.defaultView && !this.hasChildNodes()) {
var flag = document.createElement("img");
flag.src = "https://flagcdn.com/24x18/" + this._countryCode + ".png";
this.appendChild(flag);
}
}
}
customElements.define("flag-icon", FlagIcon);
</script>
</html>
Note: images may take time to load depending on the internet speed.
Let me know if I've missed anything.
I am trying to use Country/City dropdowns in multiple places of my project, however I am getting stuck with an unusual behaviour - when I am changing the countries. How can I make one request to get the local JSON file and populate multiple dropdowns?
Here is my code:
$(document).ready(function() {
fetch('assets/test.json').then(response => {return response.json()}).then(selectData => {
console.log(selectData)
function updateSelectsBirth() {
let citiesBirth = selectData[this.value].map((key, val) => {
return $("<option />").text(key).val(key);
});
$("#cityBirth, #cityBirth1").empty().append(citiesBirth);
}
let stateBirth;
$countryBirth = $("#countryBirth, #countryBirth1").on("change", updateSelectsBirth);
for (stateBirth in selectData) {
$("<option />").text(stateBirth).val(stateBirth).appendTo($countryBirth);
}
$countryBirth.change();
})
});
And the HTML
<select id="countryBirth"></select>
<select id="cityBirth"></select>
<br/>
<select id="countryBirth1"></select>
<select id="cityBirth1"></select>
Here's also a link to the demo project: link to the demo
The unexpected behaviour comes from
$("#cityBirth, #cityBirth1").empty().append(citiesBirth);
Because, when it's updating the cities, it's updating all the dropdowns instead of just one.
So you can try:
$(document).ready(function() {
fetch('assets/test.json').then(response => {return response.json()}).then(selectData => {
// console.log(selectData)
function updateSelectsBirth(event) {
let citiesBirth = selectData[this.value].map((key, val) => {
return $("<option />").text(key).val(key);
});
// console.log(event.data.target)
$(event.data.target).empty().append(citiesBirth);
}
let stateBirth;
$countryBirth = $("#countryBirth").on("change", {target: "#cityBirth"}, updateSelectsBirth);
$countryBirth1 = $("#countryBirth1").on("change", {target: "#cityBirth1"}, updateSelectsBirth);
// $countryBirth1 = $("#countryBirth1").on("change", updateSelectsBirth("#cityBirth1"));
for (stateBirth in selectData) {
$("<option />").text(stateBirth).val(stateBirth).appendTo($countryBirth);
$("<option />").text(stateBirth).val(stateBirth).appendTo($countryBirth1);
}
$countryBirth.change();
$countryBirth1.change();
})
});
I apologize this is not a complete answer, as I'm not generalizing to multiple dropdowns, however I am not able to leave comments yet. I hope this can still be helpful.
I am showing the json object on the UI when user click on a link, below is the exception it is showing
TypeError: Cannot read property '$filter' of undefined
Demo: http://plnkr.co/edit/5NOBQ3rzE3EFwTnuLbXG?p=preview
sample js code:
app.controller('BaseCtrl', ['$scope','$http','$filter', function($scope,$http,$filter) {
$scope.myList = [{"sId":100,"spread":"21x","owner":"Michael","labels":"deffered incomplete"},
{"sId":101,"spread":"34","owner":"Steve","labels":"complete"},
{"sId":102,"spread":"90s","owner":"John","labels":"tested"},
{"sId":103,"spread":"332","owner":"Dex","labels":"complete deffered"}
];
//$scope.myListObj;
$scope.showList = function(myListObj){
console.log("myListObj " + JSON.stringify(myListObj));
// $scope.defferedList = $scope.myListObj.filter(function( obj ) {
$scope.defferedList = JSON.stringify($scope.myListObj).filter(function( obj ) {
console.log(obj.labels.includes('deffered'));
return obj.labels.includes('deffered');
});
console.log("defferedList :: " + $scope.defferedList);
}
}]);
I have included '$http','$filter' in the controller.Any inputs?
I made a quick change, as I think this is what you actually want.
Basically, you aren't using $filter, and you're trying to filter on a type that does not have that method within its prototype. If you don't stringify, it will work as intended.
var app = angular.module('myApp', []);
app.controller('BaseCtrl', ['$scope','$http', function($scope,$http) {
$scope.myList = [{"sId":100,"spread":"21x","owner":"Michael","labels":"deffered incomplete"},
{"sId":101,"spread":"34","owner":"Steve","labels":"complete"},
{"sId":102,"spread":"90s","owner":"John","labels":"tested"},
{"sId":103,"spread":"332","owner":"Dex","labels":"complete deffered"}
];
//$scope.myListObj;
$scope.showList = function(myListObj){
console.log("myListObj " + JSON.stringify(myListObj));
// $scope.defferedList = $scope.myListObj.filter(function( obj ) {
$scope.defferedList = myListObj.filter(function( obj ) {
console.log(obj.labels && obj.labels.includes('deffered'));
return obj.labels && obj.labels.includes('deffered');
});
console.log("defferedList :: ", $scope.defferedList);
}
}]);
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="script.js"></script>
<style>
.my-col-50{float:left;}
</style>
</head>
<body ng-app="myApp" ng-controller="BaseCtrl">
<div class="container">
<a href="javascript:void(0)" ng-click="showList(myList)">
Click here</a>
</div>
</body>
</html>
This can be corrected by writing the showList method as below
$scope.showList = function(myListObj) {
console.log("myListObj " + JSON.stringify(myListObj));
$scope.defferedList = myListObj.filter(function(obj) {
console.log(obj.labels.includes('deffered'));
return obj.labels.includes('deffered');
});
console.log("defferedList :: " + $scope.defferedList);
}
The error was basically because you were trying to apply the filter operation intended for arrays type on the Stringified version of $scope.myListObj instead of actual array.
Also since you are using filter method of array object, you would not require "$filter" as dependency for the module.
Your problem isn't with $filter The error I am seeing is
Cannot read property 'filter' of undefined
This is referring to the javascript method filter not AngularJS's '$filter'. Filter is a javascript method that can be called on an array. It also doesn't appear that there a need to do JSON.stringify here. Try this instead
$scope.defferedList = $scope.myListObj.filter(function( obj ) {
console.log(obj.labels.includes('deffered'));
return obj.labels.includes('deffered');
});
Been researching for the last couple of days and still no result. I have a back-end database that saves text entered in the Monaco-Editor. However, text will not display/load if there are new line returns (\r\n) in the string. The only I can get the text to display is to remove the line returns.
Here is some client-side code.
<div id="container" style="width:590px;height:400px;border:1px solid grey;white-space:pre-wrap;"></div>
//saving to hidden value
<input type="hidden" runat="server" id="editorValue" />
require.config({ paths: { 'vs': '../node_modules/monaco-editor/min/vs' } });
require(["vs/editor/editor.main"], function () {
var editor = monaco.editor.create(document.getElementById('container'), {
value: ['<%=MyJSText%>'].join('\n'),
language: 'javascript'
});
jQuery(document).ready(function ($) {
jQuery("#<%=linkOK.ClientID%>").on('click', function () {
getVal = editor.getValue();
document.getElementById("<%=editorValue.ClientID%>").value = getVal;
});
});
Some server side code
protected string MyJSText
{
get
{
if (EnableIDEditor)
{
return Server.HtmlDecode(TemplateRevision.JsScripts.Replace(Environment.NewLine, " "));
}
else
{
return Server.HtmlDecode(TemplateRevision.JsScripts);
}
}
}
I'd like the text entered into the monaco-editor box to display with line-breaks. Any help would be greatly appreciated.
Finally solved the problem by adding the following code on the server-side.
protected string MyJSText
{
get
{
if (EnableIDEditor)
{
return Server.HtmlDecode(TemplateRevision?.JsScripts?.Replace(System.Environment.NewLine, "\\\r\\n"));
}
else
{
return Server.HtmlDecode(TemplateRevision.JsScripts);
}
}
}
The trick was to use .Replace(System.Environment.NewLine, "\\r\n")) and the line breaks showed appropriately. Hope this helps someone who is looking to dynamically insert text into the monaco-editor.
I am trying to extract the json values and put them into a html table.Although i am successful in inserting the data into the table,i couldnot insert them vertical.Below mentioned screenshot explains the problem.
In the table, the values that are being printed in the row are meant to be displayed vertically.i.e they come under the column name.
Attached is the structure of my JSON file.
{
"meta":
{
"view":
{
"columns":[
"cachedContents":{
"name:"name"
}]}}}
more details are available at:
https://data.baltimorecity.gov/api/views/hyq3-8sxr/rows.json
I am trying to print the values that are present in the name column. Below posted is the code.
<html>
<head>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
<style>
#result
{
color:red;
}
</style>
</script>
<script type="text/javascript">
$(document).ready(function()
{
var row = $("<tr/>")
$("#personDataTable").append(row);
$.get("https://data.baltimorecity.gov/api/views/hyq3-
8sxr/rows.json",function(data)
{
$.each(data.meta.view,function(i,obj)
{
if(i=="name")
$("#results").append("<p align='center'>"+obj+"
</p>");
if(i=="description")
$("#results").append("<p align='center'>"+obj+"
</p>");
if(i=="columns")
var length = data.meta.view.columns.length;
for(var ind=0;ind<length;ind++)
{
if(null!=obj[ind].name)
{
$("#results").append("<tr>
<td>"+"name"+obj[ind].name+"</tr></td>");
}
if (null!=obj[ind].cachedContents)
{
$.each(obj[ind].cachedContents,function(j,obj1)
{
if (j=="top")
{
var toplength = obj1.length;
for (k=0;k<toplength;k++)
{
if(null!=obj1[k].item)
{
$("#results").append("<li>"+obj1[k].item+"
</li>");
if(obj[ind].name=="name")
{
row.append($("<td
align='vertical'>" + obj1[k].item + "</td>"));
}
if(null!=obj1[k].item.human_address)
{
$("#results").append("
<li>"+obj1[k].item.human_address+"</li>");
}
}
}
}
}
);
}
}
I am trying to print the values that are present in the name column vertically
Any Suggestions would be highly helpful.