How do I create copyable html code with Angular JS - javascript

I have an array of objects called sections. Each object (section in sections) has properities like name, cssclass and more. Ex:
$scope.sections = [
{ title 'first section', cssclass: 'red'},
{ title: 'second section', cssclass: 'blue'}
];
What would be the best way to output some HTML-Code in the view that any user can copy?
Let's say for example that it outputs exactly
<section class="red"> <h1> first section </h1></section>
<section class="blue"> <h1>second section</h1></section>`
and so on for looping for all the objects that the sections array may have.
Just to clarify one more time, I'd like a textarea (or something similar) where the html isn't processed but shown to the user in raw form

One can use 'pass' in $compile to do directive-processing at will and then do whatever you want with with the angularJS-generated HTML. In addition, one has to supply a unique scope based on the user's model input to the new element, which can be accomplished with $rootScope.$new(). In the example below, the model format is expected to be JSON, so that it doesn't like, explode, but if one is creating this for personal use one could allow simple JS input and use eval(), allowing the user to write a more sophisticated model.
Here it is in action: http://jsbin.com/taqoqi/1/
var module = angular.module('module', []);
module.directive('xxAngulizer', function($compile, $rootScope) {
return {
restrict: 'A',
template: '<div>TEMPLATE</div><textarea id="template" ng-model="template" ng-change="update"></textarea>' +
'<div>MODEL</div><textarea id="model" ng-model="model" ng-change="update"></textarea>' +
'<div>HTML OUTPUT</div><textarea id="output" ng-model="output"></textarea>' +
'<div id="hidden" ng-hide="true"></div>',
scope: true,
link: function($scope, elem) {
var templateElem = $(elem.find('#template'));
var modelElem = $(elem.find('#model'));
var outputElem = $(elem.find('#output'));
var hiddenElem = $(elem.find('#hidden'));
$scope.template = '<div ng-repeat="cat in cats">{{cat.name}} the famous {{cat.breed}}</div>';
$scope.model = '{ "cats": [{ "name": "Fussy", "breed": "Russian Blue" },' +
' { "name": "Juniper", "breed": "Maine Coon" },' +
' { "name": "Chives", "breed": "Domestic Shorthair" }] }';
$scope.output = '';
$scope.update = update;
var $magicScope;
function update() {
var model, template;
try {
model = JSON.parse($scope.model);
} catch (err) {
model = null;
$scope.output = 'Model is not valid JSON.';
}
if (model) {
try {
template = $($scope.template);
} catch (err) {
console.log(err);
template = null;
$scope.output = 'Template is not valid(ish) HTML.';
}
}
if (template) {
if ($magicScope) { $magicScope.$destroy(); }
$magicScope = $rootScope.$new(true);
for (var p in model) {
$magicScope[p] = model[p];
}
//$scope.$apply(function() {
$compile(hiddenElem.html(template))($magicScope);
//});
//$scope.$apply(function(){
// $scope.output = hiddenElem.html();
//});
setTimeout(function(){
$scope.output = hiddenElem.html();
$scope.$apply();
}, 500);
}
}
$scope.$watch('template', update);
$scope.$watch('model', update);
setTimeout(update, 500);
}
};
});
<!DOCTYPE html>
<html>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
<body ng-app="module">
<div xx-angulizer></div>
</body>
</html>
Hope this help!

Related

ng-click inside the ng-repeat binding all the data with ng-include

I want to include the html page with the help of ng-click inside the ng-repeat.
But, it loading all the content for all ng-repeat elements.
My requirement was I want to bind(ng-include) only the clicked element.
Please find the attachment for your reference.
HTML
<body ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="message in messages">
<a ng-click="clickMe()">Clike Me</a>
<span>{{ message.text }}---{{ message.type }}</span>
<div ng-include="templateUrl"></div>
</div>
</body>
JS
var app = angular.module('myApp', []);
app.controller('myCtrl', function ($scope) {
var messages = [
{ text: "Standard Message", type: "success000" },
{ text: "Success Message!", type: "success111" },
{ text: "Alert Message!", type: "alert222" },
{ text: "secondary message...", type: "secondary333" }
]
$scope.name = 'shiva';
$scope.count = 0;
$scope.messages = messages;
$scope.clickMe = function () {
// alert('clicked');
$scope.count++;
$scope.templateUrl = "Page.html";
};
});
page.Html
<b> Included html Code.{{message.type}}---count={{count}}
Add to message new property clicked. Set ng-include only when message.clicked is true using ng-if. And when you click, change state of message.clicked on true
Somthing like this:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl',function($scope){
var messages = [
{ text: "Standard Message", type: "success0" ,clicked:false},
{text:"Success Message!", type:"success00",clicked:false},
{text:"Alert Message!", type : "alert2",clicked:false},
{text:"secondary message...", type : "secondary3",clicked:false}
]
$scope.name = 'shiva';
$scope.count = 0;
$scope.messages = messages;
$scope.clickMe = function (message) {
// alert('clicked');
$scope.count++;
message.clicked=true;
$scope.templateUrl = "Page.html";
};
});
</script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="message in messages">
<a ng-click="clickMe(message)">Clike Me</a>
<span>{{message.text}}---{{message.type}}</span>
<div ng-if="message.clicked" ng-include="templateUrl"></div>
</div>
</body>
</html>
First you need to add an id your json data as follows:
var messages = [
{ id: "101", text: "Standard Message", type: "success0", clickCount: 0 },
{ id: "102", text: "Success Message!", type: "success00", clickCount: 0 },
{ id: "103", text: "Alert Message!", type: "alert2", clickCount: 0 },
{ id: "104", text: "secondary message...", type: "secondary3", clickCount: 0 }
]
Next you need to run a filter and find the item on ng-click
$scope.clickMe = function (obj) {
$scope.messages.forEach(function (value, key) {
if (value.id === obj.id) {
value.isClicked = true;
value.clickCount++;
}
});
$scope.count++;
$scope.templateUrl = "Page.html";
};
After that, make changes into your Page.html
<p class="info">{{ name }}--{{ message.type }}--{{ message.text }}</p>
<p class="element">
Current Count = ({{ message.clickCount }}), Total Count = ({{ count }})
</p>
At last you need to make the following two HTML changes
1) Add the object in ng-click
<a ng-click="clickMe(message)">Clike Me</a>
2) Specify the condition on ng-include
<div ng-include="templateUrl" ng-if="message.isClicked"></div>
Demo

Link div and textbox value on div selection

I am trying to sync div with textbox.
For example, I created 2 nodes i.e Node 1 and Node 2 and when I select node1 and I enter title and location then title and location should sync with Node1 so when I enter title while I have selected node1 then title value should get reflected for node1 and after entering title and location next time when I select node1 then title and location value should be reflected in my textbox.
I created this below Fiddle to demonstrate the problem : http://jsfiddle.net/quk6wtLx/2/
$scope.add = function (data) {
var post = data.nodes.length + 1;
var newName = data.name + '-' + post;
data.nodes.push({ name: newName, nodes: [],selected : false });
};
$scope.tree = [{ name: "Node", nodes: [], selected: false }];
$scope.setActive = function (data) {
clearDivSelection($scope.tree);
console.log(data);
data.selected = true;
};
I am not getting how to do this.
You need to bind the form elements with the data you are appending to the tree.
Check this snippet
var app = angular.module("myApp", []);
app.controller("TreeController", function ($scope) {
$scope.delete = function (data) {
data.nodes = [];
};
$scope.add = function (data) {
var post = data.nodes.length + 1;
var newName = data.name + '-' + post;
data.nodes.push({ name: newName, nodes: [],selected : false, myObj: { name: newName} });
};
$scope.tree = [{ name: "Node", nodes: [], selected: false }];
$scope.setActive = function ($event, data) {
$event.stopPropagation();
$scope.selectedData = data;
clearDivSelection($scope.tree);
data.selected = true;
};
function clearDivSelection(items) {
items.forEach(function (item) {
item.selected = false;
if (item.nodes) {
clearDivSelection(item.nodes);
}
});
}
});
ul {
list-style: circle;
}
li {
margin-left: 20px;
}
.active { background-color: #ccffcc;}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app="myApp" ng-controller="TreeController">
<li ng-repeat="data in tree" ng-include="'tree_item_renderer.html'"></li>
<script type="text/ng-template" id="tree_item_renderer.html">
<div ng-class="{'active': data.selected}" > {{data.myObj.name}}</div>
<button ng-click="add(data)">Add node</button>
<button ng-click="delete(data)" ng-show="data.nodes.length > 0">Delete nodes</button>
<ul>
<li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'" ng-click="setActive($event, data)"></li>
</ul>
</script>
<div style="margin-left:100px;">
Title : <input type="text" ng-model="selectedData.myObj.name" />
Location : <input type="text" ng-model="selectedData.myObj.location" />
</div>
</ul>
You can check the binding documentation for AngularJs and all the possibilities https://docs.angularjs.org/guide/databinding

Filtering complex object inside nested ng-repeat

I want to filter object inside nested ng-repeat.
HTML:
<div ng-controller="MyController">
<input type="text" ng-model="selectedCityId" />
<ul>
<li ng-repeat="shop in shops">
<p ng-repeat = "locations in shop.locations | filter:search" style="display: block">
City id: {{ locations.city_id }}
<span style="padding-left: 20px; display: block;" ng-repeat="detail in locations.details | filter:item">Pin code: {{detail.pin}}</span>
</p>
</li>
</ul>
Controller:
var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope) {
$scope.search = function (location) {
if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
return true;
}
if (location.city_id === parseInt($scope.selectedCityId)) {
return true;
}
};
$scope.item = function (detail) {
if ($scope.selectedCityId === undefined || $scope.selectedCityId.length === 0) {
return true;
}
if (detail.pin == parseInt($scope.selectedCityId)) {
return true;
}
};
$scope.shops =
[
{
"category_id":2,
"locations":[
{
"city_id":368,
"details": [{
"pin": 627718,
"state": 'MH'
}]
}
]
},
{
"name":"xxx",
"category_id":1,
"locations":[
{
"city_id":400,
"region_id":4,
"details": [{
"pin": 627009,
"state": 'MH'
},{
"pin": 129818,
"state": 'QA'
}]
},
]
},
];
});
Here's the fiddle:
http://jsfiddle.net/suCWn/210/
I want to use multiple filter inside ng-repeat.
Example: Whenever user enters the ID in the input box. The list should filter based on cityID or PinCode.
if user enter '129818' it should show pin code result of 129818 along with its parent cityID
Similarly, if a user enter 400, the list should filter and show cityID result with 400 along with its child pin code.
EDIT:
Update Code http://codepen.io/chiragshah_mb/pen/aZorMe?editors=1010]
First, you must not filter locations with matching details. Use something like this in the search filter:
$scope.search = function (location) {
var id = parseInt($scope.selectedCityId);
return isNaN(id) || location.city_id === id ||
location.details.some(function(d) { return d.pin === id });
};
To show details if filtered by cityID, you have to find the parent location and check if it was filtered.
$scope.item = function (detail) {
var id = parseInt($scope.selectedCityId);
return isNaN(id) || detail.pin === id || locationMatches(detail, id);
};
function locationMatches(detail, id) {
var location = locationByDetail(detail);
return location && location.city_id === id;
}
function locationByDetail(detail) {
var shops = $scope.shops;
for(var iS = 0, eS = shops.length; iS != eS; iS++) {
for(var iL = 0, eL = shops[iS].locations.length; iL != eL; iL++) {
if (shops[iS].locations[iL].details.indexOf(detail) >= 0) {
return shops[iS].locations[iL];
}
}
}
}
EDIT Another, more flexible solution would be to remove all the filters from ngRepeats and do the filtering in a method that you call on ngChange of the search text. Here is the basic structure for this approach.
myApp.controller('MyController', function($scope, $http) {
var defaultMenu = [];
$scope.currentMenu = [];
$scope.searchText = '';
$http.get(/*...*/).then(function (menu) { defaultMenu = menu; } );
$scope.onSearch = function() {
if (!$scope.searchText) {
$scope.currentMenu = defaultMenu ;
}
else {
// do your special filter logic here...
}
};
});
And the template:
<input type="text" ng-model="searchText" ng-change="onSearch()" />
<ul>
<li ng-repeat="category in currentMenu">
...
</li>
</ul>
I have updated your filters. The problem is in your search filter you are only checking for the city_id, what you should do is:
Check if the typed id is city_id
Check if typed id is a pid of a child detail of given location
Similar thing for the item filter:
Check if the typed id is a pid of the detail being filtered
Check if the typed id is a city_id of the parent location of the detail passed in
Here is a working jsFiddle. I hope this helps.
By simply modifying the JSON to include the city_id for children so you don't need to loop through it to get the parent's city_id, the solution is as easy as this:
var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope) {
$scope.search = function (location) {
if (!$scope.selectedCityId)
return true;
//if user's input is contained within a city's id
if (location.city_id.toString().indexOf($scope.selectedCityId) > -1)
return true;
for (var i = 0; i < location.details.length; i++)
//if user's input is contained within a city's pin
if (location.details[i].pin.toString().indexOf($scope.selectedCityId) > -1)
return true;
};
$scope.item = function (detail) {
if (!$scope.selectedCityId)
return true;
//if user's input is contained within a city's id
if (detail.city_id.toString().indexOf($scope.selectedCityId) > -1)
return true;
//if user's input is contained within a city's pin
if (detail.pin.toString().indexOf($scope.selectedCityId) > -1)
return true;
};
Modified JSON
$scope.shops=[{"category_id":2,"locations":[{"city_id":368,"details":[{"city_id":368,"pin":627718,"state":'MH'}]}]},{"name":"xxx","category_id":1,"locations":[{"city_id":400,"region_id":4,"details":[{"city_id":400,"pin":627009,"state":'MH'},{"city_id":400,"pin":129818,"state":'QA'}]},]},];});
If directly modifying the JSON is not possible, you can modify it like this in this controller directly after this $scope.shops = ...json... statement:
for(var i=0; i<$scope.shops.length; i++)
for(var j=0, cat=$scope.shops[i]; j<cat.locations.length; j++)
for(var k=0, loc=cat.locations[j]; k<loc.details.length; k++)
loc.details[k].city_id=loc.city_id;
Working fiddle:
http://jsfiddle.net/87e314a0/
I tried to make the solution easier to understand :
index.html :
<div ng-controller="MyController">
<input type="text" ng-model="search.city_id" />
<ul>
<li ng-repeat="shop in shops">
<p ng-repeat = "locations in shop.locations | filter:search.city_id" style="display: block">
City id: {{ locations.city_id }}
<span style="padding-left: 20px; display: block;" ng-repeat="detail in locations.details | filter:item">Pin code: {{detail.pin}}</span>
</p>
</li>
</ul>
</div>
app.js :
var myApp = angular.module('myApp', []);
myApp.controller('MyController', function ($scope) {
$scope.shops =
[
{
"category_id":2,
"locations":[
{
"city_id":368,
"details": [{
"pin": 627718,
"state": 'MH'
}]
}
]
},
{
"name":"xxx",
"category_id":1,
"locations":[
{
"city_id":400,
"region_id":4,
"details": [{
"pin": 627009,
"state": 'MH'
},{
"pin": 129818,
"state": 'QA'
}]
},
]
},
];
});
Here's the fiddle :
mySolution

Add item to array Angular

I have a table with these fields: product, lot, input1, input2. You can clone a line, and you can add a new line.
What I want to do is that for each row you can add a new Lot created by a "number" and by "id" that user write in the input field under the Select lot. And I wanted that the script add the new Lot in the json data and the lot 's option list.
This is the function for add that I tried to do:
$scope.addLot = function() {
var inWhichProduct = row.selectedProduct;
var newArray = {
"number": row.newLot.value,
"id": row.newLot.id
};
for (var i = 0; i < $scope.items.length; i++) {
if ($scope.items[i].selectedProduct === inWhichProduct) {
$scope.items[i].selectedLot.push(newArray);
}
}
};
-->> THIS <<-- is the full code.
Can you help me?
I think your question is a little too broad to answer on Stack Overflow, but here's an attempt:
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<tr ng-repeat="lot in lots">
<td>{{ lot.id }}</td>
<td>{{ lot.name }}</td>
</tr>
</table>
<p>name:</p> <input type="text" ng-model="inputName">
<p>id:</p> <input type="text" ng-model="inputId">
<button ng-click="addLotButton(inputId, inputName)">Add</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.lots = [{
name: "test",
id: 1
},
{
name: "test2",
id: 2
}
];
$scope.addLot = function(lotId, lotName) {
var newLotObject = {
name: lotName,
id: lotId
};
$scope.lots.push(newLotObject);
};
$scope.addLotButton = function(id, name) {
$scope.addLot(id, name);
};
$scope.addLot(3, "Another test");
});
</script>
Basically this code just takes some input and adds an object to the scope for that input. The table is created using an ng-repeat of this data. It's not great code at all but it's just a quick example.
The push method adds newArray to selectedLot array. It's not working on the JSON data but on arrays. If you want to have the JSON, you can give a try to :
var myJsonString = JSON.stringify(yourArray);
It will create a JSON string based on the parameter
Maybe you should try to structure your data to make lots as properties of products.
{
products: [
{id: 1, lots: [{id:1}, {id:2}]},
{id: 2, lots: [{id:1}, {id:2}]}
]
}
To add a lot to a product :
product = products[0];
product.lots.push(newArray);
Change the fallowing:
html:
<button ng-click="addLot(row.selectedProduct.id,row.newLot.value,row.newLot.id)">Add</button>
js:
$scope.addLot = function(id,val,lotId) {
// console.log(id);
var inWhichProduct = id;
var newArray = { "value": val, "id": lotId };
//console.log($scope.items)
angular.forEach($scope.items,function(v,i){
if($scope.items[i].id == id )
{
$scope.items[i].lots.push(newArray);
console.log($scope.items[i].lots);
}
});
};
http://plnkr.co/edit/W8eche8eIEUuDBsRpLse?p=preview

Why doesn't my simple Ember.js Handlebars helper work when loading data asynchronously?

I have a simple Handlebars helper which simply formats a money value. The helper works property when I test with static data, but not when I load data asynchronously. In other words, {{totalBillable}} will output the expected amount, but {{money totalBillable}} will output zero. But only when the data is loaded via an ajax call. What the heck am I doing wrong?
I've tried to pare the code down as much as possible, and also created a jsfiddle here:
http://jsfiddle.net/Gjunkie/wsZXN/2/
This is an Ember application:
App = Ember.Application.create({});
Here's the handlebars helper:
Handlebars.registerHelper("money", function(path) {
var value = Ember.getPath(this, path);
return parseFloat(value).toFixed(2);
});
Model:
App.ContractModel = Ember.Object.extend({});
App Controller:
App.appController = Ember.Object.create({
proprietor: null,
});
Contracts Controller (manages an array of contracts):
App.contractsController = Ember.ArrayController.create({
content: [],
totalBillable: function() {
var arr = this.get("content");
return arr.reduce(function(v, el){
return v + el.get("hourlyRate");
}, 0);
}.property("content"),
When the proprietor changes, get new contract data with an ajax request. When getting data asynchronously, the handlebars helper does not work.
proprietorChanged: function() {
var prop = App.appController.get("proprietor");
if (prop) {
$.ajax({
type: "POST",
url: '/echo/json/',
data: {
json: "[{\"hourlyRate\":45.0000}]",
delay: 1
},
success: function(data) {
data = data.map(function(item) {
return App.ContractModel.create(item);
});
App.contractsController.set("content", data);
}
});
}
else {
this.set("content", []);
}
}.observes("App.appController.proprietor")
});
If I use this version instead, then the Handlebars helper works as expected:
proprietorChanged: function() {
var prop = App.appController.get("proprietor");
if (prop) {
var data = [{
"hourlyRate": 45.0000}];
data = data.map(function(item) {
return App.ContractModel.create(item);
});
App.contractsController.set("content", data);
}
else {
this.set("content", []);
}
}.observes("App.appController.proprietor")
View:
App.OverviewTabView = Ember.TabPaneView.extend({
totalBillableBinding: "App.contractsController.totalBillable"
});
Kick things off by setting a proprietor
App.appController.set("proprietor", {
ID: 1,
name: "Acme"
});
Template:
<script type="text/x-handlebars">
{{#view App.OverviewView viewName="overview"}}
<div class="summary">
Total Billable: {{totalBillable}}<br/>
Total Billable: {{money totalBillable}}<br/>
</div>
{{/view}}
</script>
when using a helper, handlebars does not emit metamorph tags around your helper call. this way, this part of the template is not re-rendered because there is no binding
to manually bind part of a template to be re-rendered, you can use the bind helper:
<script type="text/x-handlebars">
{{#view App.OverviewView viewName="overview"}}
<div class="summary">
Total Billable: {{totalBillable}}<br/>
Total Billable: {{#bind totalBillable}}{{money this}}{{/bind}}<br/>
</div>
{{/view}}
</script>

Categories

Resources