I have old project. In this moment i can not rewrite all to use ng-view and routes. So i have large html file and many unreadable code.
<div ng-if="f1">
<div>...</div>
</div>
<div ng-if="f2">
<div>...</div>
</div>
<div ng-if="f3">
<div>...</div>
</div> ....etc
I would like to break this code into blocks and use the ng-include to clean code. But then I will have a lot of this tag(> 10). Is it normal? Is there a way to reorganize the file differently?
<div ng-if="f1" ng-include="url1"> </div>
<div ng-if="f2" ng-include="url2"> </div>
<div ng-if="f3" ng-include="url2"> </div>
You should put your logic in an array in controller like this
$scope.paths = [
{url : "url1" , condition: $scope.f1},
{url : "url2" , condition: $scope.f2},
{url : "url3" , condition: $scope.f3},
];
And then use it in html like this
<div ng-repeat="item in paths"> <div ng-if="item.condition" ng-include="item.url"></div> </div>
You can create an array object and use ng-repeat on it.
HTML:
<div ng-repeat="template in templates">
<div ng-if="template.f" ng-include="template.url"> </div>
</div>
JS
//Array of objects that contain your checks and template urls
$scope.templates = [{
"url": value,
"f": value
}, {
"url": value,
"f": value
}, .....,
{
"url": value,
"f": value
}];
It is good to use ng-route but if you are not comfortable then
here is one hack.
Create one json data like this:
$scope.myAllTemplate = [{
"isShown":false, "url":"/myTemplateURL1","templateName":"Template1"},{
"isShown":false, "url":"/myTemplateURL2","templateName":"Template2"},{
"isShown":false, "url":"/myTemplateURL3","templateName":"Template3"}
]
Rendering the name of template from where you have to toggle the ng-if by click event
<div ng-repeat="item in myAllTemplate ">
<anyTag ng-click="changeTemplate(item)">item.templateName</anyTag>
</div>
Controller function
$scope.changeTemplate = function(data){
data.isShown = true;
//here you can handle the template according to your wish
}
Finally, render the template
<div ng-repeat="item in myAllTemplate ">
<div ng-if="item.isShown" ng-include="item.url"></div>
</div>
Related
I have a webpage that list a lot of elements (movies to be specific), the HTML structure of every item is in some way large and complicated (divs, images, links, CSS class, etc).
Firstly I load 100 elements and the user have the option of load the next 100 (this is made using infinite scroll): by now, I make a AJAX petition requesting the another 100 elements and it responds with a HTML text (with all of them loaded) and I just append it to the document.
But, now I don't want to respond with the HTML text, instead of that I want to respond with the 100 elements data in a JSON (I can do that), then, my question is: Which is the best way to add these elements to the document using Javascript?
I know that I can loop over the JSON array and construct every element, but as I said, it's a large HTML structure and I don't really want to create divs and then attach it to another div,set CSS classes, etc with Javascript, because it might get disordered,messy and very large...So, there's a way in javascript to achieve this maybe using something like templates? How can I do that? I just want to get a clean and better code.
The structure of every movie is like this (can I use it like a template?):
<div data-section="movies" data-movie_id="myid" id="movie-id" class="movie anotherclass">
<img src="myImageUrl">
<div class="aCSSclass">
<div class="aCSSclass">
<div class="aCSSclass"></div>
<div class="aCSSclass">
<div class="aCSSclass">
Movie title
</div>
<div class="details form-group">
<a class="aCSSclass" href="myHref">Details</a>
<button onclick="SomeFunction" class="aCSSclass">My button</button>
<div class="aCSSclass"><span class="icon star"></span><span class="aCSSclass"></span><span class="aCSSclass"></span><span class="aCSSclass"></span><span class="aCSSclass"></span></div>
</div>
</div>
</div>
</div>
</div>
The answer is to make a template and then copy the node using cloneNode(). Append all the cloned nodes to a documentFragment to save time on drawing and finally append it to the page.
An approach to this:
var movies = {"movie1" : { "title" : "Die Hard", "imageurl" : "example.com/image.jpg", "details" : "http://example.com", "func" : "functionname" },
"movie2" : { "title" : "Die Hard 2", "imageurl" : "example.com/image.jpg", "details" : "http://example.com", "func" : "functionname" },
"movie3" : { "title" : "Die Hard With 3", "imageurl" : "example.com/image.jpg", "details" : "http://example.com", "func" : "functionname" }
};
function functionname()
{
alert("NYI");
}
var keys = Object.keys(movies); //get the keys.
var docFrag = document.createDocumentFragment();
for (var i = 0; i < keys.length; i++)
{
var tempNode = document.querySelector("div[data-type='template']").cloneNode(true); //true for deep clone
tempNode.querySelector("div.title").textContent = movies[keys[i]].title;
tempNode.querySelector("img").src = movies[keys[i]].imageurl;
tempNode.querySelector("button").onclick = window[movies[keys[i]].func];
tempNode.querySelector("a").href = movies[keys[i]].details;
tempNode.style.display = "block";
docFrag.appendChild(tempNode);
}
document.body.appendChild(docFrag);
delete docFrag;
<!-- template -->
<div style="display: none" data-type="template" data-section="movies" data-movie_id="myid" id="movie-id" class="movie anotherclass">
<img src="myImageUrl">
<div class="aCSSclass">
<div class="aCSSclass">
<div class="aCSSclass"></div>
<div class="aCSSclass">
<div class="aCSSclass title">
Movie title
</div>
<div class="details form-group">
<a class="aCSSclass" href="myHref">Details</a>
<button onclick="SomeFunction" class="aCSSclass">My button</button>
<div class="aCSSclass"><span class="icon star"></span><span class="aCSSclass"></span><span class="aCSSclass"></span><span class="aCSSclass"></span><span class="aCSSclass"></span></div>
</div>
</div>
</div>
</div>
</div>
This is just an example, not based upon your actual JSON. However you can easily clone a template and then fill in the values.
Use
document.querySelector
document.querySelectorAll
document.createDocumentFragment
Element.cloneNode(bool)
I have a complex object as shown below:
$scope.document =
{
"GENERAL_FIELDS": {
"Source_Type": "custom",
"Annotations": [
"216/content/Factiva_CM_001/Proteins",
"216/content/Factiva_CM_001/Fact"
],
"Content": [
" Baculovirus; Budded virus; Ultrastructure; Cryo-EM;"
],
"Title": [
"Budded baculovirus particle structure revisited"
]
},
"stn": {
"Document_Type": [
"Journal",
"Article"
]
}
}
I want to display all the fields present in "GENERAL_FIELDS" and "stn". Fields' value can either be string or array of strings. If it is array, I further want to ng-repeat on it and display the content. Following is my html:
<div id="titsec" class="comdocdet" ng-repeat="(category, group) in document">
<div ng-repeat="(key, value) in group">
<div class="pTitle">
{{key}}
</div>
<div class="contdesc">
<div ng-if="Array.isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!Array.isArray(value)">
{{value}}
</div>
</div>
</div>
</div>
But ng-if="Array.isArray(value)" is never true and array fields are being displayed in object form: ["Journal","Article"]. What am I missing ?
Or add this in your controller and leave rest like it is.
$scope.isArray = angular.isArray;
html would be like this :
<div ng-if="isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!isArray(value)">
{{value}}
</div>
Instead of accessing a method on the Array object directly in the template, you should do in your controller. So for example:
<div ng-if="vm.isValueAnArray(value)">
// Some html
</div>
Your controller:
function isValueAnArray(val) {
return Array.isArray(val);
}
I haven't tested it, but logic should be in the controller, not in the template.
This is an issue of Scoping
The scope of the template is relative to $scope in the controller, so when it looks for Array, it will look for that in the controller scope (e.g. $scope.Array).
One option is to use ng-if="window.Array.isArray(value)". See the working example below.
Another option is to set $scope.Array = Array.prototype in the controller. That way there is no need to reference window before calling Array.isArray().
Another option is to create an alias for Array.isArray() in the controller scope:
$scope.isValueAnArray = Array.isArray;
Then call that function to determine if the value is an array.
angular.module('ang', [])
.controller('cont', function($scope) {
//use this to avoid referencing window in the template
//$scope.Array = Array.prototype;
$scope.document = {
"GENERAL_FIELDS": {
"Source_Type": "custom",
"Annotations": [
"216/content/Factiva_CM_001/Proteins",
"216/content/Factiva_CM_001/Fact"
],
"Content": [
" Baculovirus; Budded virus; Ultrastructure; Cryo-EM;"
],
"Title": [
"Budded baculovirus particle structure revisited"
]
},
"stn": {
"Document_Type": [
"Journal",
"Article"
]
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ang" ng-controller="cont">
<div id="titsec" class="comdocdet" ng-repeat="(category, group) in document">
<div ng-repeat="(key, value) in group">
<div class="pTitle">
{{key}}
</div>
<div class="contdesc">
<div ng-if="window.Array.isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!window.Array.isArray(value)">
{{value}}
</div>
</div>
</div>
</div>
</div>
Having difficulty to assign ng-model values inside ng-repeat
So i am repeating this div with an array of json objects. I can print the 'ID' value of the object inside any element. But i can't use that as the ng-model value for the checkbox inside. I must be doing something wrong here. Any idea what that is?
Will really appreciate it if someone can take a look.
Here is a codepen of the issue. Code pen link
.
value for the model that assign to the checkbox is boolean whether it is true or false, unless you define the value. but again it is only 2 options value.
so, rather than using id as model attribute, you might change it to some attribute that could store boolean value. why not using 'isSelected'
<div ng-controller="quoteController" ng-app="MyApp" class="benefits-container">
<!-- benefits -->
<div class="benefit" ng-class="{'selected': pe.id}" ng-repeat="pe in policyEnhancementsArr | filter: {type:'optional'}">
<div class="top">
<md-checkbox ng-model="pe.isSelected" class="blue"></md-checkbox>
<h5 class="item">{{pe.name}}</h5>
<h5 class="prize">{{pe.loading}}</h5>
</div>
<div class="bottom">
<p>{{pe.limitDisplay}}</p>
</div>
</div>
</div>
then update some isSelected value:
...
{
"id": "PVC022",
"name": "NCD Protector",
"limit": null,
"limitDisplay": "N/A",
"desc": "<TBC>",
"type": "optional",
"loading": 0.0,
"isSelected": true
},
...
i have done exactly the same code the difference is the filter that you applied on ng-bind. try reading the article i suggest use ng-value.
whats the difference between ng-model and ng-value
try using ng-repeat and ng-model withing the same line.
instead of this
<div class="benefit" ng-class="{'selected': pe.id}" ng-repeat="pe in policyEnhancementsArr | filter: {type:'optional'}">
<div class="top">
<md-checkbox ng-model="pe.id" class="blue"></md-checkbox>
use this
<div class="benefit" ng-class="{'selected': pe.id}" ng-repeat="pe in policyEnhancementsArr | filter: {type:'optional'}"
ng-model="pe.id">
<div class="top">
<md-checkbox class="blue"></md-checkbox>
I am trying to display a binary tree of elements, which I go through recursively with ng-include.
What is the difference between ng-init="item = item.left" and ng-repeat="item in item.left" ?
In this example it behaves exactly the same, but I use similiar code in a project and there it behaves differently. I suppose it's because of Angular scopes.
Maybe I shouldn't use ng-if, please explain me how to do it better.
The pane.html is:
<div ng-if="!isArray(item.left)">
<div ng-repeat="item in [item.left]" ng-include="'Views/pane.html'">
</div>
</div>
<div ng-if="isArray(item.left)">
{{item.left[0]}}
</div>
<div ng-if="!isArray(item.right)">
<div ng-repeat="item in [item.right]" ng-include="'Views/pane.html'">
</div>
</div>
<div ng-if="isArray(item.right)">
{{item.right[0]}}
</div>
<div ng-if="!isArray(item.left)">
<div ng-init = "item = item.left" ng-include="'Views/pane.html'">
</div>
</div>
<div ng-if="isArray(item.left)">
{{item.left[0]}}
</div>
<div ng-if="!isArray(item.right)">
<div ng-init="item = item.right" ng-include="'Views/pane.html'">
</div>
</div>
<div ng-if="isArray(item.right)">
{{item.right[0]}}
</div>
The controller is:
var app = angular.module('mycontrollers', []);
app.controller('MainCtrl', function ($scope) {
$scope.tree = {
left: {
left: ["leftleft"],
right: {
left: ["leftrightleft"],
right: ["leftrightright"]
}
},
right: {
left: ["rightleft"],
right: ["rightright"]
}
};
$scope.isArray = function (item) {
return Array.isArray(item);
}
});
EDIT:
First I run into the problem that the directive ng-repeat has a greater priority than ng-if. I tried to combine them, which failed. IMO it's strange that ng-repeat dominates ng-if.
It's a little hacky, but I am passing variables to an ng-include with an ng-repeat of an array containing a JSON object :
<div ng-repeat="pass in [{'text':'hello'}]" ng-include="'includepage.html'"></div>
In your include page you can access your variable like this:
<p>{{pass.text}}</p>
Pass parameter to Angular ng-include
You don't need that. all ng-include's sources have the same controller. So each view sees the same data.
What is the difference between ng-init="item = item.left" and ng-repeat="item in item.left"
[1]
ng-init="item = item.left" means - creating new instance named item that equals to item.left. In other words you achieve the same by writing in controller:
$scope.item = $scope.item.left
[2]
ng-repeat="item in item.left" means create list of scopes based on item.left array. You should know that each item in ng-repeat has its private scope
I am trying to display a binary tree of elements, which I go through recursively with ng-include.
I posted in the past answer how to display tree by using ng-include.
It might helpful: how-do-display-a-collapsible-tree
The main part here that you create Node with id wrapped by <scipt> tag and use ng-repeat:
<script type="text/ng-template" id="tree_item_renderer">
<ul class="some" ng-show="data.show">
<li ng-repeat="data in data.nodes" class="parent_li" ng-include="'tree_item_renderer'" tree-node></li>
</ul>
</script>
<ul>
<li ng-repeat="data in displayTree" ng-include="'tree_item_renderer'"></li>
Making a generic directive instead of ng-include is a cleaner solution:
Angular passing scope to ng-include
I am using ng-include with ng-repeat of an array containing string. If you want to send multple data so please see Junus Ergin answer.
See my code Snippet:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="">
<div ng-repeat="name in ['Sanjib Pradhan']" ng-include="'your_template.html'"></div>
<div ng-repeat="name in ['Chinmay Sahu']" ng-include="'your_template.html'"></div>
<script type="text/ng-template" id="your_template.html">
{{name}}
</script>
</div>
So I have the next div in index.html:
...
<div ng-include src="'templates/blocks.html'" ng-controller='Foo'></div>
...
The Foo controller holds an array of html snippets under the "bars" property.
The blocks.html looks like:
<div class="block" ng-repeat="bar in bars">
<div ng-bind-html-unsafe="{{bar}}"></div>
</div>
and what I'm getting is the html as text in the div with class "block", meaning it is not evaluated as html.
thanks.
ng-bind-html-unsafe is removed in 1.2. Use ng-bing-html instead and remove the curly braces just like Sebastian said.
Include ngSanitize module as a dependancy to your app and dont forget to include its JS
//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular-sanitize.js.
Remove the curly braces in ng-bind-html-unsafe and it should work:
<div class="block" ng-repeat="bar in bars">
<div ng-bind-html-unsafe="bar"></div>
</div>
Just for completeness - here is a sample jsfiddle.
<div ng-repeat="item in tree" ng-include="'a.html'" class="node"></div>
a.html
<p> {{item.name}}</p>
controller.js
$scope.tree = [
{name: 'Узел', nodes: [] },
{name: 'Морской', nodes: []},
{name: 'Устричный', nodes: []}
];