Append HTML in angular js giving errors - javascript

I am a newbie in angularjs and was trying to append the data in the view on click event.
The following is the code I have written:
Html
<div id="space-for-buttons">
<input type="hidden" id="hdnTopRatedPage" value="0" ng-update-hidden>
<div class='item' ng-repeat='p in programs.rated' home-tab-item watchable="p" ></div>
</div>
<div class = 'rated-spin' ng-show = 'programs.rated == 0 && ratedLoaded == false'> </div>
<div ng-show="programs.rated == 0 && ratedLoaded == true" class="noResultsBlock">No Results for Rated.</div>
<div class='clearfix'></div>
<div ng-controller="MainCtrl" ng-app="woi">
<yes style='background-color: brown;padding-right: 61px;'></yes>
</div>
Directive:
var pageno=1;
function MainCtrl($scope) {
$scope.count = 0;
}
//Directive that returns an element which adds buttons on click which show an alert on click.Here we can put
woi.directive("yes",['userAPI','$rootScope', function(userAPI,$rootScope){
return {
restrict: "E",
template: "<a ng-click='str()' addbuttons>please click on me</a>"
}
}]);
//Directive for adding buttons on click that show an alert on click
woi.directive("addbuttons",['$rootScope','userAPI','$compile', function($rootScope,userAPI,$compile){
return function(scope, element, attrs){
element.bind("click", function(){
scope.count++;
alert("hII")
//var abc= scope.loadRated(pageno);
userAPI.topRated({userid: $rootScope.getUser().userid}, function (r, $scope, div){
if(!$rootScope.device.isMobile && !$rootScope.device.isTablet && !$rootScope.device.isTouch ) {
var topRatedList = r.gettopratedhomepage.topratedprogrammelist;
console.log(r)
var str='';
for(var i=0;i<topRatedList.length;i++)
{
// str+="topRatedList[i].actualname";
var programNameEncode= topRatedList[i].programmename // | encodeUrl;
var program=topRatedList[i].programmename;
var channelId=topRatedList[i].channelid;
var programId=topRatedList[i].programmeid;
var channel=topRatedList[i].channeldisplayname;
var channelNameEncode=topRatedList[i].channeldisplayname //| encodeUrl;
var startTime=topRatedList[i].starttime;
var favourite=topRatedList[i].isfavorite;
var reminder=topRatedList[i].isreminder;
var watchlist=topRatedList[i].iswatchlist;
var duration=topRatedList[i].duration;
var imageFile=topRatedList[i].imagefilepath;
var startTime= topRatedList[i].starttime;
var duration= topRatedList[i].duration;
var isReco=topRatedList[i].isrecommended; // This is for reco
str+=" <div class='item'>" ;
str+="<div class=\"thumb\">";
// str+="<div ng-show=\""+isReco=='1'+"\" class=\"favorite-ribbon\"></div>";
str+="<div class=\"player\"></div>";
str+="<div class='image' style='background-image:url('"+imageFile+"');'>";
str+="<img src='"+imageFile+"'/>";
str+="<a ng-click=\'playVideo("+program+"\,$event)' ng-show='hasVideo()' class='play' style='cursor:pointer'></a>";
str+= "<a ng-href=\"#!/program/"+programNameEncode+"\" ng-show=\"!hasVideo()\" class=\"noPlay\" ng-click=\"EncodeUrlWithDash("+program+",$event,'programme',"+channelId+","+programId+","+startTime+")\"></a></div>";
str+="<span class='time' ng-show='duration'>\""+duration+"\"</span>";
str+="<div class='user-actions' ng-controller='UserController'>";
str+="<a live-tooltip='Add to Favorite' ng-click='toggleFavorite(p, $event)' class='btn btn-small btn-purple-blue' ng-class=\""+{active:favourite == '1'}+"\>";
str+="<i class='icon-favorite'></i></a>";
str+="<a live-tooltip='Reminder Alerts' ng-click='toggleReminder(p, $event)' class='btn btn-small btn-purple-blue' ng-class=\""+{active:reminder == '1'}+"\>";
str+="<i class='icon-reminder'></i></a>";
str+="<a live-tooltip='Add to Watchlist' ng-click='addToWatchlist(p, $event)' class='btn btn-small btn-purple-blue' ng-class=\""+{active:watchlist == '1'}+"\>";
str+="<i class='icon-watchlist'></i></a></div>"; //end of user controller div
str+="</div>"; // end of thumb div
str+="<div class='text-wrapper'>";
str+="<h2 multiline-overflow><a title='"+program+"' href='#!/program/"+programNameEncode+"' ng-click=\""+"EncodeUrlWithDash("+program+",$event,'programme',"+channelId+","+programId+","+startTime+")"+"/ >\""+program+"\"</a></h2>";
str+="<p class='infoChannel' live-tooltip-single-line= '{{channel}}'><a href='#!/channel/"+channelNameEncode+"' ng-click=\""+"EncodeUrlWithDash("+channel+",$event,'channel',"+channelId+","+programId+")"+"/>\""+channel+"\"</a></p>";
str+="<p class='info'>\""+startTime+"\"</p>";
str+="</div></div>"; //end of the main div
//EXTRA LINE
// str+=" <div class='item' ng-repeat='topRatedList[i] in programs.rated' home-tab-item watchable='topRatedList[i]' ></div>" ;
alert("The below value is of str")
alert(str);
angular.element(document.getElementById('space-for-buttons')).append($compile(str)(scope));
}
alert("The below value is of str")
// The above code is required to append data into the division(loadrated)..
}
});
});
};
}]);
however the code is running properly(ie it goes through the entire directive)..But data is not displayed ie appending.
Instead it goes into another directive which I have called hometab directive and giving issues.
I dont know why this is happening.
Please help me out

It would be much easier for you to create a Plunker for this issue here
Concerning your directive, without having full access to your files, it is considered good practice to hold all functionality and styles within the scope of it. Therefore, the only thing that should be in the HTML is the directive element only. Everything else, should thus be within the directive js.
Just as well, your ng-show/hide have very intensive expressions within them. I would try and chop'em down a bit. Additionally, the HTML syntax with quotes are using " " in some instances and ' ' with mixed spaces in others.
I have seen this throw errors in the past, though it is valid to use single or double. The preferred usage is the double quotes "" on everything in HTML and single quotes for JS files. I would just make sure you are using one or the other and not both.
You can check this article about the specific rules here

Related

Why can`t I access scope via controller?

I am working on passion project. And I cant access scope to pass data to back-end frame work.
Here is my index file
<div id="main-menu" ng-controller="appCtrl">
//some other code
<div id="includedDocumentsFilter" style="float:right; display:none; padding-right: 10px;">
<my-documents validate-options="validateDialogOptions()" call-dialog="showDialog()"> </my-documents>
</div>
//some other code
</div>
My custom directive
'use strict';
dbApp
.directive('myDocuments', [
function () {
var documentTemplate =
' <div class="caption-row">' +
'<kendo-button style="width:62px" ng-click="changeDocument(true)"> Ok </kendo-button>'+
'<kendo-button style="width:62px" ng-click="changeDocument(false)" > Revert changes </kendo-button>'+
'</div>'
}
return {
scope: true,
template: documentTemplate
}
}]
)
My controller
$scope.changeDocument = function (applyFilter) {
if (applyFilter === true) {
//Here is where I cant access $scope
}
}
Firstly, I see a extra closing curly braces in your directive. Secondly in your html code there is display:none in div with id "includedDocumentsFilter". Just wondering if you are hiding the div, how will you be able to see the template defined in your directive. I have added a working jsfiddle link below using your above mentioned code
dbApp.directive('myDocuments', [
function () {
var documentTemplate =
' <div class="caption-row">' +
'<kendo-button style="width:62px" ng-click="changeDocument(true)"> Ok </kendo-button>'+
'<kendo-button style="width:62px" ng-click="changeDocument(false)" > Revert changes </kendo-button>'+
'</div>'
return {
scope: true,
template: documentTemplate
}
}]
)
JsFiddle link: https://jsfiddle.net/anilsarkar/gk2dfh1p/21/
Note: I have replaced kendo-button with span in jsfiddle

Directive doesn't fire after changing textarea model

I have a text with newline separator and URLs:
first row\nFind me at http://www.example.com and also\n at http://stackoverflow.com.
I want to update ng-repeat values after pressing on copy button.
I have this HTML:
<div ng-controller="myCntrl">
<textarea ng-model="copy_note_value"></textarea>
<button data-ng-click="copy()">copy</button>
<div>
<p ng-repeat="row in note_value.split('\n') track by $index"
wm-urlify="row"
style="display: inline-block;"
>
</p>
</div>
</div>
Controller:
app.controller('myCntrl', function ($scope) {
$scope.note_value = "first row\nFind me at http://www.example.com and also\n at http://stackoverflow.com";
$scope.copy_note_value = angular.copy($scope.note_value);
$scope.copy = function(){
$scope.note_value = angular.copy($scope.copy_note_value);
}
});
I have directive that should take text and return urlfied text:
app.directive('wmUrlify', ['$parse', function ($parse) {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attrs) {
function urlify(text) {
var urlRegex = /(https?:\/\/[^\s]+)/g;
return text.replace(urlRegex, function (url) {
return '' + url + '';
})
}
var text = $parse(attrs.wmUrlify)(scope);
var html = urlify(text);
element[0].inneHtml(html)
}
};
}]);
Here is a flow: User changes text in textarea and presses on copy button. I expect to show the change in ng-repeat.
It works only if I add a new line and not line content.
What is wrong here? This is my Fiddle
Just remove the track by $index from your ng-repeat. This is because you are telling Angular that the value of note_value.split('\n') will only be changed when there is a change in the $index i.e. size of the array after splitting by new line.
But the default implementation of track by is the identity of each item. So when you changed the default implementation to track it by the $index and when you are not not adding a new line instead just updating the content of any existing line, Angular is not able to detect that there is a change.
Update
Removing the track by $index function will throw an exception when there are same values after split. So you can use a simple function like: (define it in your controller)
$scope.indexFunction = function($index, val) {
// Creating an unique identity based on the index and the value
return $index + val;
};
And then use it in your ng-repeat like:
<p ng-repeat="row in note_value.split('\n') track by indexFunction($index, row)"></p>
https://docs.angularjs.org/api/ng/directive/ngRepeat

generated AngularJS controller usage

Realized that to implement some front-end stuff in my project, it`s better to use a front-end framework, not just a pure JS. Choose AngularJS.
Trying to implement and generated code and stuck with it...
I`ve got this html code generated and and added to the page content on some action,
function Content(i){
var html =
'<div ng-controller="Hello">'+
'<button ng-click="click_btn()">Load Data!</button>'+
'<div class="panel panel-default">'+
'<div class="panel-heading">Items List</div>'+
'<div class="col-sm-8">'+
'<ul>'+
'<li ng-repeat="element in content"> {{ element }} '</li>'+
'</ul>'+
'</div>'+
'</div>'+
'</div>';
var ul = document.getElementById('FullList');
ul.insertAdjacentHTML('beforeend', html);
AddElementAngular(contentString);
}
And the problem is that AngularJS <div ng-controller="Hello"> won't work here. can't get why, because code is successfully added to the page. I'm also tried to add click_btn() action and test the controller call on this action, but still doesn't work.
Here is the controller's code
angular.module('MainPage', [])
.controller('Hello', function($scope, $http) {
alert('jijij');
$scope.click_btn = function() {
alert('fsdfsdfsd');
}
$http.get('/api/getlist?groupID=2').
success(function(data) {
$scope.content = data;
});
});
The page is big to paste it here, but it starts with <html ng-app="MainPage">, so there shouldn't be a problem.
I'm inserted alerts to check, does the code is running. with the dynamic controller load - no. If i just insert controller's code directly in page content - everything works ok.
Controller inserted to the page dynamically only once. If the function Content(i){} call made again - previous controller's code will be removed.
EDIT:
Found a way to inject generated controller to the scope, from here:
http://geevcookie.com/2014/01/compile-dynamic-html-with-angularjs/
function AddElementAngular(html){
var $div = $(html);
$(document.body).append($div);
angular.element(document).injector().invoke(function($compile) {
var scope = angular.element($div).scope();
$compile($div)(scope);
});
}
But still didn't work properly. Code in the controller is executed(
$http.get('/api/getlist?groupID=2').
success(function(data) {
alert(data);
$scope.content = data;
});
i get the [object][Object]... etc in alert. ), but the in-page <li ng-repeat="element in content"> {{ element }} </li> doesn`t work for unknown reason. I just getting '{{ element }}' in HTML content.

Dynamically adding angularjs directive with a $http to get

I am actually stuck with an interesting problem,
I am trying to make soemthing of this sort:
input key 1 , input value 1
input key 2 , input value 2 < button to add more >
< submit button >
Basically a user can click submit and issue a Get request to a given URL. now when he click to add more a new row appears with two input fields, where he can add more http get paarmeters.
I tried coding this up, but I am close to this: http://jsfiddle.net/d2jL2n35/1/
Could you please help me...
Two questions:
How to dynamically add a new row after the plus box is clicked?
myApp.directive('options',function(){
return {
restrict:"E",
template:"<div><input placeholder='params1' type='text'/><input placeholder='params2' type='text'><button><i class='glyphicon glyphicon-plus'></i></button></div>"
}
})
Ok solved the first one by using $compile: http://jsfiddle.net/KyEr3/216/
How to get all the params and issue a get request?????
Here is a very rough example:
http://jsfiddle.net/d2jL2n35/9/
The crux of it is you have to store each set of values in an array, and reuse your directive to show them. I removed your http call because I didn't want to sketch out the echo service. But if you had a real service it could be like this:
$http.get('/options').success( function( result ) {
$scope.options = result;
} );
I'm guessing that you want something like this:
Example
View:
<div ng-app="myApp" ng-controller="mainController">
<options parameters="parameters"></options>
</div>
Controller:
var myApp = angular.module('myApp',[])
.factory('yourAPI', ['$http', function ($http) {
var submitResults = function (parameters) {
return $http.get("http://wwww.whateverURL.com?" + parameters.map(function(parameter){return parameter.key + "=" + encodeURI(parameter.val) }).join('&'));
};
return {
submitResults: submitResults
}
}])
.directive('options', function(){
return{
restrict:"E",
replace:true,
template:
"<div><option parameter='parameter' ng-repeat='parameter in parameters'></option>"+
"<div>"+
"<input type='text' placeholder='key' ng-model='newKey' /> " +
"<input type='text' placeholder='value' ng-model='newVal' /> " +
"<button ng-click='addItem()'><i class='glyphicon glyphicon-plus'></i></button>" +
"</div>" +
"<input type='submit' value='submit' ng-click='sendRequest()' /></div>",
scope: { parameters:'='},
controller: function($scope, yourAPI){
$scope.newKey="";
$scope.newVal="";
$scope.addItem = function(){
$scope.parameters.push({key:$scope.newKey, val:$scope.newVal});
$scope.newKey="";
$scope.newVal="";
};
$scope.sendRequest = function(){
yourAPI.submitResults($scope.parameters).success(function(data, status, headers) {
console.log(data);
});
}
}
}
})
.directive('option', function(){
return {
restrict:'E',
require: '^options',
replace: true,
scope: { parameter:'='},
template: "<div>"+
"<input type='text' placeholder='key' ng-model='parameter.key' />" +
"<input type='text' placeholder='value' ng-model='parameter.val' /></div>"
}
})
.controller('mainController', function($scope){
$scope.parameters=[];
});
Notice that jsFiddle will block the get request, but this should work in your app.
It looks like you're ready to upgrade to LEVEL 2 AWESOME ANGULAR HACKER!
Since your directives are so small, it might be easier as a beginner to only have one directive, instead of two. Directives are really great tools, but can make things more complex.
To get the data out of the inputs, you can use ng-model (https://docs.angularjs.org/api/ng/directive/ngModel)
So after copy-pasting your options' template into your values template, you can put a click event, that is ng-click=addParam() on the button.
HTML:
<button ng-click="addParam()>
JS:
$scope.param1 = null;
$scope.param2 = null;
$scope.addParam = function () {
$http.get('some url', {param1: param1, param2: param2}).success(data) {
// add another option row
};
}
In this function, you're going to want to add another option row. One way to do this is have a for loop that creates a div for each ng-repeat, so you're template will look like this:
<div ng-repeat="n in noptions">
<input placeholder="params1" ng-model="param1" />
<button ng-click="addParam()>
</div>
To add another option row, you can then add 1 to noptions, like $noptions +=1
https://docs.angularjs.org/api/ng/directive/ngRepeat
$scope.noptions = 0;
$http.get('some url', {param1: param1, param2: param2}).success(data) {
$scope.noptions +=1;
};
and another option row will be dynamically added.

How do I leave static content if property is empty in angular?

I have a simple angular controller in my app. And I wish to enhance a page containing static content, with angular. This will allow me to have a good, content rich page as fall back for those without JS (who?!) and web crawlers and readers.
So far I have identified a few ways to do this, possibly using custom directives, or by doubling up content in 'ng-bind-template' directive (which is obviously not ideal).
I am fairly new to the world of angular so would like to try and garner the best practice for this scenario.
What is the best approach for this?
Controller:
app.controller('TestArticle',function($scope, feed){
$scope.initValue = "This is angular injecting this"
$scope.activate = function(){
$scope.eTitle = "Dynamic title";
$scope.eContent = "Dynamic content";
};
});
Markup:
(The problem here is that 'Static content' is replaced by '' if angular initializes)
<div ng-controller="TestArticle">
<div ng-cloak ng-bind-template='{{eTitle}}'>Static content</div>
<div ng-cloak ng-bind-template='{{eContent}}'>Some more content</div>
<div ng-cloak>{{initValue}}</div>
<a href ng-click="activate()" ng-cloak>click to activate</a>
</div>
EDIT
I should be clear that even though angular is bootstrapping, The aim is to leave the default content intact, and not replace it with dynamic content. That only wants to be achieved once the activate button is clicked. I have tried the following but it involves doubling up the content which could get bulky if it is a whole article.
<div ng-controller="TestArticle">
<div ng-cloak ng-bind-template='{{eTitle || "Static Content"}}'>Static content</div>
<div ng-cloak ng-bind-template='{{eContent || "Some more content"}}'>Some more content</div>
<div ng-cloak>{{initValue}}</div> <a href ng-click="activate()" ng-cloak>click to activate</a>
</div>
You can create a simple directive to create the html structure that you want.
I propose this to be your new html:
<div ng-cloak sk-custom-bind="eTitle">Static content</div>
Notice how you don't have to specify the or clause with the static content.
Now you can create a directive that will build the html template for you:
app.directive('skCustomBind',function() {
return {
restrict: 'A',
template: function(elem, attrs) {
var templ = "<div ";
templ += "ng-bind-template='{{";
templ += attrs.skCustomBind;
templ += ' || "';
templ += elem.text();
templ += '"';
templ += "}}'></div>";
return templ;
},
replace: true
};
});
And as simple as that you have the functionality that you are looking for without the duplication.
See this plunker to see a working sample.
The thing here is that when you define the controller, angular will parse through the html to find all the variables and link them with the corresponding controller items using $scope.
So, when angular initializes, since eTitle and eContent are not having any values (since you initialize them in the function only), which is called on click of the last item(where on your application works fine).
So until you click the button, the static text is replaced by $scope.eTitle and $scope.eContent both of which are undefined. Hence no text inside the tags.
So the solution,
1. Either you initialize the variables inside the controller with the static text, like you did for 'This is angular injecting this'(reason only this came!):
app.controller('TestArticle',function($scope, feed){
$scope.initValue = "This is angular injecting this";
$scope.eTitle = "Dynamic title";
$scope.eContent = "Dynamic content";
$scope.activate = function(){
$scope.eTitle = "Dynamic title";
$scope.eContent = "Dynamic content";
};
});
Or you can initialize the variables in the html using ng-init, it will initialize the variable in html only:
<div ng-controller="TestArticle">
<div ng-init="eTitle = 'Static Content'" ng-cloak ng-bind-template='{{eTitle}}'>Static content</div>
<div ng-init="eContent = 'Some more content'" ng-cloak ng-bind-template='{{eContent}}'>Some more content</div>
<div ng-cloak>{{initValue}}</div>
<a href ng-click="activate()" ng-cloak>click to activate</a>
</div>
The thing is when you start using angular, the html will get kind of hijacked and angular will start using its own variables.
Thanks to #JoseM for putting me on to right track and helping me find the following solution...
SOLUTION
My Markup:
NOTE: the custom directive and properties ('cg-dyn' and 'cg-content'). These will not initialise any values when angular bootstraps. And further down you will see how these map from controller scope via an isolated scope.
<div ng-controller="TestArticle">
<div ng-cloak cg-dyn cg-content="eTitle">Static content</div>
<div ng-cloak cg-dyn cg-content="eContent">Some more content</div>
<div ng-cloak>{{initValue}}</div>
<a href ng-click="upDateTitle()" ng-cloak>Update title</a> | <a href ng-click="upDateContent()" ng-cloak>Update content</a>
</div>
My Controller:
app.controller('TestArticle',function($scope, feed){
$scope.initValue = "This is angular injecting this"
$scope.upDateTitle = function(){
$scope.eTitle = "Dynamic title";
};
$scope.upDateContent = function(){
$scope.eContent = "Dynamic Content Lorem ipsum";
};
});
And finally the magic in My Directive
app.directive('cgDyn',function() {
return {
restrict: 'A',
scope:{
'content':'=cgContent'
// this is a two way binding to the property defined in 'cg-content
},
link: function(scope, elem, attrs) {
// below is a watcher keyed on a combination content
scope.$watch('content',function(){
if(scope.content){
elem.html(scope.content);
// and here is the bit, normally plumbed by default, but instead we only replace the html if the value has been set
}
});
},
controller: 'TestArticle',
replace: false
};
});
Thanks for everyone's help. I am sure there is an even better solution out there so keep the suggestions coming!

Categories

Resources