I'm developing an app with the Ionic framework based on angularjs. I'd like to let generate HTML elements or components from a JSON file. These are buttons, lists, labels, etc.
My JSON objects look like this:
[
{
"category": "communicationPage",
"type": "Button",
"id": "communicationButton",
"icon": "ion-chatboxes",
"name": "Communication",
"onClick": "window.location.href='communicationPage.html'",
"ngclick": "open()",
"ngcontroller": "openctrl",
"color": "white",
"background-color": "#ff5db1",
"font-size": "20px"
},
{
"category": "servicePage",
"type": "Button",
"id": "serviceButton",
"icon": "ion-briefcase",
"name": "Service",
"onClick": "window.location.href='servicePage.html'",
"color": "blue",
"background-color": "#009900",
"font-size": "26px"
}
]
I can access via my Controller on the JSON file and parse as follows:
myApp.controller('generateHTMLCtrl', function ($scope, $http) {
$http.get('myJSONFile.json').success(function(data){
$scope.components = data;
//...
});
});
The code translates of course nothing.
My question is, how can I adapt my JavaScript code so that from a
JSON object following HTML element is generated?:
<button style="color: blue; background-color: #ff5db1; font-size: 20px" onclick="window.location.href='communicationPage.html'" id="communicationButton" class="button">
<i class="ion-chatboxes"></i> <br> Communication
</button>
Once my JSON object is located always in the JSON file, should always be created the HTML element on the page.
The second question is how I can position this generated HTML
element just in my HTML page?
I want that the HTML element is generated between the responsive grid element, such as:
<div class="row responsive-sm">
<div class="col">
<!-- The Button should be generated hier-->
</div>
</div>
The third and final question is how I can let generate the HTML
element on the appropriate page? Such as: If in JSON object the key-value pair of "category": "communicationPage" occurs should the corresponding HTML element be created on 'communicationPage.html'
I would look forward to an example. Many thanks.
For the two first point, use the data-binding and ng-repeat : directive
<div class="row reponsive-sm" ng-controller="generateHTMLCtrl">
<div ng-repeat="component in components">
<button style="color : {{component.color}}; background-color : {{component.background-color}} ... ">
</button>
</div>
</div>
For the last point, I'm not sure if it's possible with AngularJS ...
Related
I have HTML that is rendered based on loaded metadata. It ends up forming a table of data based on a scoped value in the controller.
In the controller:
$scope.arrayOfCustomObjects = [{ entry: data1 },{ entry: data2 }];
The metadata is contained in a .json file, with the following format (I added two here, in reality there are hundreds, each one basically ends up describing an HTML element):
loadedMetaData:
{
"field_1": {
"index": 0,
"type": "selectbox",
"nameId": "arrayOfCustomObjects" <-- this is the string name of a scoped variable in a controller.
},
"field_2": {
"index": 1,
"type": "textinput",
"nameId": "textField"
}
}
And the HTML looks like this:
<div ng-repeat="field in loadedMetaData>
<div ng-repeat="item in field.nameId">
<!-- build out HTML for each -->
</div>
</div>
When I run this, it doesn't work (it never iterates over $scope.arrayOfCustomObjects). If I add a line to display {{field.nameId}} is displays 'arrayOfCustomObjects' but I think it's just the string, not the value it represents.
If I change the HTML to this it does work:
<div ng-repeat="field in loadedMetaData>
<div ng-repeat="item in arrayOfCustomObjects">
<!-- build out HTML for each -->
</div>
</div>
...but is there any way to keep the abstraction I'm going for? I'd like to be able to define the target array in the metadata so I don't have to define the controller-specific names in the HTML itself.
Suppose that your loadedMetadata is like:
[
"arrayOfCustomObjects",
// other 'data' object keys
]
Then you could have an object like this that stores your actual data:
data = {
"arrayOfCustomObjects": [
// some items
]
}
Then, in the template you do:
<div ng-repeat="field in loadedMetaData>
<div ng-repeat="item in data[field]">
<!-- display the items in arrayOfCustomObjects -->
</div>
</div>
This takes the value as string
var field = { "nameId": "arrayOfCustomObjects" }
change this to
var field = { "nameId": arrayOfCustomObjects }
Use a bracket notation property accessor with the this keyword:
<div ng-repeat="(fieldKey, fieldValue) in loadedMetaData>
<div ng-repeat="item in this[fieldValue.nameId]">
<!-- build out HTML for each -->
</div>
</div>
From the Docs:
It is possible to access the context object [($scope)] using the identifier this and the locals object using the identifier $locals.
— AngularJS Developer Guide - Expression Context
To achieve expected result, use below changes from your plunker code
Using scope function to convert string to scope variable
Rendering that variable inside inner ng-repeat
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.arrayOfCustomObjects = [1,2,3]
$scope.loadedMetaData = {
"field_1": {
"index": 0,
"type": "selectbox",
"nameId": "arrayOfCustomObjects"
},
"field_2": {
"index": 1,
"type": "textinput",
"nameId": "textField"
}
}
$scope.getVariable = function(val){
return $scope[val]
}
});
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl as ctrl">
<div>
<div ng-repeat="field in loadedMetaData">
<div ng-repeat="item in getVariable(field.nameId)">
{{item}}
</div>
</div>
</div>
</div>
</body>
</html>
codepen - https://codepen.io/nagasai/pen/XGLZzX
I'm new to AngularJS. I have a web service which returns a list of cdn links for fonts. I want to use it and show some text using the fonts in run time. My client application is written in angularJS. How can I achieve this? Please help. This is what I have tried so far. How can I inject font faces on demand?
app.js (data returned by web service is shown as a list here.)
var app = angular.module( 'app', [] );
app.controller('appController', function($scope) {
$scope.fonts = [
{
"family": "Abhaya Libre",
"url": "https://cdn.rawgit.com/mooniak/abhaya-libre-font/gh-pages/fonts/AbhayaLibre-Regular.otf"
},
{
"family": "Gemunu Libre",
"url": "https://cdn.rawgit.com/mooniak/gemunu-libre-font/gh-pages/tests/fonts/GemunuLibre-Regular.otf"
},
];
});
app.html
<div ng-app="app" ng-controller="appController">
<p ng-repeat="font in fonts">
<span style="#font-face {font-family:{{font.family}}; src: url({{font.url}});}">
{{font.family}}
</span>
</p>
</div>
JSFiddle - https://jsfiddle.net/lpsandaruwan/rpffoo06/
Here is the Solution, in the style you are not putting font-family:{{font.family}};
http://pastebin.com/WmBv5XDB
I want to show the content of my json model in a dynamic way, depending on the provided json. I use ng-repeat to loop through my data and want to display a html template to fill with data dependent on the encountered data type.
JSON
{
"elements": [
{
"type": "input-text",
"desc": "Full Name"
},
{
"type": "input-checkbox",
"desc": "Accept Terms"
}
]
}
This should result in different html code, appropriate filled with the json content.
E.g.
<div><label>Full Name</label> <input type="text"></div>
<div><input type="checkbox"> <label>Accept Terms</label></div>
Right now what I do is to use an angularjs directive to create an element and add the json values to the right spot. e.g. element.html('<div><input type="checkbox"> <label>' + scope.item.desc + '</label></div>') That seems like the jquery way (or worse) to do it although I want to do it the 'right' angularjs way.
How can I use a different HTML template filled with content, dependent on the encountered JSON data?
PS: The above example is a simple one, the encountered data is far more complex than switching the position of the label and input field.
All you need to do is set the data on the scope, then use the ng-repeat directive in your HTML to output it:
Controller:
.controller('MyData', function ($scope) {
$scope.myModel = {
elements: [ { desc: .. }, .. ]
};
})
You would be using the $http service or some other appropriate method in this controller to populate myModel with data, but in the end the data needs to end up on the $scope object somehow. Then it's the template's job to display that data:
<div ng-controller="MyData">
<ul>
<li ng-repeat="element in myModel.elements">
{{ element.desc }}
</li>
</ul>
</div>
A simple solution seems to use ngSwitch with different HTML paths, e.g.:
<div ng-switch="item.type">
<div ng-switch-when="input-text">
<div><label>{{item.desc}}</label> <input type="text"></div>
</div>
<div ng-switch-when="input-checkbox">
<div><input type="checkbox"> <label>{{item.desc}}</label></div>
</div>
<div ng-switch-default>Unknown item.type: {{item.type}}</div>
</div>
Seems the approach using an angularjs directive (which I took first) may be a good solution for complex scenarios as "Huy Hoang Pham" points out in his blog post: http://onehungrymind.com/angularjs-dynamic-templates/ (thanks!)
I'm new to jsTree.How to apply custom css for jsTree div id like background color,node font style etc., any sample examples will be helpful
<div id="sampleTree"></div>
load jstree method
$('#sampleTree').jstree({
'core' : {
'data' : {
'url' : 'ajaxurl',
'data' : function (node) {
var test = ["jquery", "js", "css"];
return test;
}
}
}
});
since jstree is a totally javascript generated code, adding your own class would be not advisable, since adding it while the rendering time trough the jstree engine would make the system more complex. What you can do is, trace the classes in the themes/default/style.css
and make changes in the classes. some of them are .jstree hovered, clicked and so on.
If you want to put CSS onto individual entries use the html_data plugin. You can put it in your HTML strings with embedded CSS directly:
<div id="myTree">
<ul>
<li rel="root">
Parent 1
<ul>
<li><a style="font-weight:bold" href="#">Child 1</a></li>
<li><a style="color: red" href="#">Child 2</a></li>
</ul>
...
Yes you can change the theme css file according to you.
If you want more customization like changing the icons when open, when close etc then you can do it in jquery
$('#jstree-api').jstree({
'core': {
'data': jsonData
},
"types": {
"child": {
"icon": "glyphicon glyphicon-leaf"
},
"root": {
"icon": "glyphicon glyphicon-folder-close"
},
"default": {
"icon": "glyphicon glyphicon-folder-close"
}
},
"search": {
"case_insensitive": true,
"show_only_matches": true
},
"plugins": ["search", "themes", "types"]
});
$('#jstree-api').on('open_node.jstree', function (e, data) {
data.instance.set_icon(data.node, "glyphicon glyphicon-folder-open");
}).on('close_node.jstree', function (e, data) { data.instance.set_icon(data.node, "glyphicon glyphicon-folder-close"); });
Here is a series of articles on jsTree you can follow if you want.
I have a bootstrap accordion that obtains it's header info from JSON, within each accordion pane I have a table, and the information for each table also get's populated with JSON.
The issue I have is that the all of the table data populates within the first accordion pane.
It does not move on to the secondary table and populate the information in there, my JSON data does include ID's so it is possible to navigate between the items just not sure how.
Here is Some of the code:
<div class="well">
</div>
<div data-bind="attr: {id: 'collapse'+$index()}" class="accordion-body collapse">
<div class="accordion-inner">
<div id="injectbody">
<table class="table table-striped">
<thead>
<tr>
<th>ContentID</th>
<th>Content</th>
<th>Qty To Assign</th>
</tr>
</thead>
<tbody data-bind="foreach: Locations">
<tr>
<td id="Lot" data-bind="text: ContentID"></td>
<td id="Area" data-bind="text: Content"></td>
<td id="QtyToAssign">
<input type="text" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
And the JQuery to make it all work:
var data = {
"d": [{
"__type": "Sample",
"ItemID": "1",
"ItemName": "First Item",
"QtyUnassigned": 10
}, {
"__type": "Sample",
"ItemID": "2",
"ItemName": "Second Item",
"QtyUnassigned": 15
}]
};
var data2 = {
"d": [{
"__type": "Sample2",
"ItemID": 1,
"ContentID": "1",
"Content": "This Is The First Item Content"
}, {
"__type": "Sample2",
"ItemID": 2,
"ContentID": "2",
"Content": "This Is The Second Item Content"
}]
};
var ProductViewmodel;
//debugger;
//Binds ViewModel
function bindProductModel(Products) {
var self = this;
self.items = ko.mapping.fromJS([]);
ProductViewmodel = ko.mapping.fromJS(Products.d, self.items);
console.log(ProductViewmodel());
}
//Binds First DataSet
function bindModel(vm, data) {
var self = this;
vm.Locations = ko.mapping.fromJS(data.d);
console.log(ProductViewmodel());
}
//Starting Function
$(document).ready(function () {
bindProductModel(data);
bindModel(ProductViewmodel()[0], data2);
ko.applyBindings(ProductViewmodel);
});
I have also created this Fiddle to demonstrate what I am trying to get to.
Your error is that since your ViewModel is actually an array, you are only binding Locations to the first element of your variable ProductViewmodel here.
bindModel(ProductViewmodel()[0], data2);
This means you have something like...
[0].Locations = [],
[1].Locations = undefined
Thus throwing an error when binding your markup (see the console in your fiddle).
In a related note, your variable naming is extremely misleading. ProductViewmodel is an array, yet you name it as ViewModel and you applyBindings to it.
I would recommend you to give Learn KnockoutJS a review. Also, stick to conventions, when variable naming pick camelCase, or underscore_case, or PascalCase or something, but just don't mix them. Finally, if you have functions that do something only applicable for specific objects, try to use a better name other than bindModel like bindLocationsToModel.