Readonly is not working in AngularJs - angularjs

Inside my HTML file:
<rating ng-show = "onerate =='null'" readonly="isReadOnly" ng-model="newRate.rating" max="max" class="assertive" ng-click="addRating('{{singleRecipe[0].recipe_id}}')" >
</rating>
I want that whenever I click once it will never be clickable.
Inside my controller in my Javascript file:
$scope.rate = 1;
$scope.max = 5;
$scope.isReadonly = false;
$scope.newRate = {};
$scope.newRate.userID = $scope.userdata.user_id;
$scope.addRating = function(recipe_id){
$scope.newRate.recipeID = recipe_id;
RatingList.add($scope.newRate);
console.log($scope.newRate);
$scope.isReadonly = true;
}

You should use ng-readonly docs here
Keep in mind that readonly attribute only works for input objects, if you are creating some element that responds to click, you will have to handle this differently.
What you really want is to change your click method:
$scope.addRating = function(recipe_id){
if (!$scope.isReadonly)
{
$scope.newRate.recipeID = recipe_id;
RatingList.add($scope.newRate);
console.log($scope.newRate);
$scope.isReadonly = true;
}
}
You use ng-class with $scope.isReadonly to dynamically add classes to visually show that the element is no longer clickable if you wanted to. More info here

Use ng-readonly
https://docs.angularjs.org/api/ng/directive/ngReadonly
You now have only readonly

Related

Restrict AngularJS ng-style

I'm calculating containers heights as per viewport by ng-style through my custom method.
Everything works well, but it keeps calling the method even if the element is styled. I have a big amount of DOM elements that are need to be styled. That's why, I can't allow continuous execution for all elements. Please note, I can't use ng-class because each element contains different content. And can't use controller scope variable due to unlimited numbers of elements.
HTML:
<div class="myElement" ng-style="styleElement('myElement')">
...
...
</div>
Function:
$scope.styleElement = function (elementCls) {
var elementToSet = $('.'+elementCls+':visible');
if(elementToSet.length){
var winHeight = $( window ).height();
var eTop = elementToSet.offset().top;
if(eTop == 0){
var elemChilds = elementToSet;
var elemChildsLen = elemChilds.length;
for(var i=0;i<elemChildsLen;i++){
var elem = elemChilds[i];
var r = elem.getBoundingClientRect();
if(r.top != 0){
eTop = r.top;
i= elemChildsLen;
}
}
}
var nScrollHeight = winHeight - eTop - 20;
return {
'height': nScrollHeight + 'px',
'overflow-x': 'hidden',
'overflow-y': 'auto'
};
}
};
I've tried using a custom directive but binding DOM or writing a watcher isn't a preferable solution for me due to performance. Thanks in advance!
Use one time binding, which will only call styleElement once.
<div class="myElement" ng-style="::styleElement('myElement')">
...
...
</div>

setting false to a scope variable doesn't work when set from factory

I have a navigation dropdown and I want to hide some of the options in the dropdown based on a value from a factory. I am using ng-show on the options that I want to hide or show based on the value from factory.
When I set it to false directly without the factory it works and I don't see those options in the dropdown.
This is how I get the data from factory:
$scope.mstrClientLStatus=userDetailsFactory.getUserDetailsFromFactory().mstrClientLStatus;
console.log($scope.mstrClientLStatus)
//it is false but doesn't hide
When I set the above to false directly the options are hidden.
$scope.mstrClientLStatus= false //works and the options are hidden
I console.log the $scope.mstrClientLStatus in the next line and it is false but it still shows the options in the dropdown.
The html:
<li ng-show="mstrClientLStatus"> //this is what I want to hide
The entire factory code:
.factory('userDetailsFactory',function(){
var user = {};
return {
setUserDetailsInFactory : function(val,$cookies,favoriteCookie,put,get){
user.useridFromSession = val[0].UserId;
user.usernameFromSession = val[0].UserName;
user.userroleFromSession = val[0].Role;
user.clientidFromSession = val[0].ClientId;
user.ip = "http://192.168.2.100:5000";
user.mstrDashBoardSession = val[0].DashBoard;
user.mstrClientLSession = val[0].ClientLogin;
user.ClientRegistrationSession = val[0].ClientRegistration;
user.mstrBustypeSession = val[0].mstrBustype;
user.mstrBusCategorySession = val[0].mstrBusCategory;
user.mstrSeatLayoutSession = val[0].mstrSeatLayout;
user.mstrDesignationSession = val[0].mstrDesignation;
user.mstrBusSession = val[0].mstrBus;
user.mstrRouteSession = val[0].mstrRoute;
user.mstrBranchSession = val[0].mstrBranch;
user.mstrDeviceSession = val[0].mstrDevice;
user.mstrServiceSession = val[0].mstrService;
user.mstrRouteFareSession = val[0].mstrRouteFare;
user.mstrNewUserSession = val[0].mstrNewUser;
user.mstrPDAServiceAssignSession = val[0].mstrPDAServiceAssign;
user.mstrUserRightSession = val[0].mstrUserRight;
user.rptTripSheetSession = val[0].rptTripSheet;
user.rptTripSummarySession = val[0].rptTripSummary;
user.rptCargoSummaryFromSession = val[0].rptCargoSummary;
user.rptBusMonthSession = val[0].rptBusMonth;
user.rptDeviceSession = val[0].rptDevice;
//console.log(user.useridFromSession);
//console.log(user.usernameFromSession);
//console.log(user.userroleFromSession);
},getUserDetailsFromFactory : function(){
return user;
}
};
My colleague suggested this and it worked although it is a workaround ..I think:
if(userDetailsFactory.getUserDetailsFromFactory().mstrClientLStatus=="true"){
$scope.mstrClientLStatus= true;
}
This is pretty much weird, giving you are writing code in controller, maybe a problem of digest cycle, all you need to put your operation inside timeout block to get it register in digest cycle,
$timeout(function() {
$scope.mstrClientLStatus = userDetailsFactory.getUserDetailsFromFactory().mstrClientLStatus;
$scope.$apply()
})

How can I get ng-click to function with ng-repeat and ng-bind-html?

I am trying to get a ng-click directive to function within an ng-repeat and ng-bind-html. The ng-click code is added to a string of html from data pulled from the server (hence the ng-bind-html). The setup has a controller, a base template that is put onto the page with Drupal, and a partial that is loaded via the template from Drupal.
The controller looks like this at the moment:
var guideListController = angular.module('app')
.controller('guideListController', [
'$scope',
'$sce',
'$compile',
'ViewService',
'ToolKit',
function($scope, $sce, $compile, ViewService, ToolKit) {
// Storage for which rows need more/less links
this.rowIndex = [];
this.showFull = false;
this.showFullClick = function() {
alert('Showfull');
};
this.trustIntro = function(code) {
return $sce.trustAsHtml(code);
};
// Fetch the guide list view from services
var data = ViewService.get({view_endpoint:'guide-service', view_path: 'guide-list'}, function(data) {
//console.log(data);
// Update/process results
for (var row in data.results) {
// Create short intro w/ truncate.js
data.results[row].Intro_short = $sce.trustAsHtml($scope.guideList.getShortIntro(data.results[row], row));
//data.results[row].Intro_short = $scope.guideList.getShortIntro(data.results[row], row);
// Update intro
data.results[row].Introduction = $sce.trustAsHtml($scope.guideList.updateIntro(data.results[row], row));
//data.results[row].Introduction = $scope.guideList.updateIntro(data.results[row], row);
}
$scope.guideList.guides = data.results;
});
// Add a read less anchor tag at the end of the main intro
this.updateIntro = function(row, row_index) {
var intro = row['Introduction'].trim();
if ($scope.guideList.rowIndex[row_index]) { // only apply Less link if needed
var index = intro.length - 1;
var tag = [];
if (intro.charAt(index) === '>') { // we have a tag at the end
index--;
do {
tag.push(intro.charAt(index));
index--;
} while (intro.charAt(index) != '/'); // the closing tag
index--; // we move the index one more for the "<"
tag.reverse(); // Reverse
tag = tag.join('');
}
var inserts = ['div', 'p']; // we insert the Less link here.
if (jQuery.inArray(tag, inserts) >= 0) { // insert into the tag
intro = intro.substr(0, index) + ' <a class="less" ng-click="$parent.guideList.showFull = false">Less</a>' + intro.substr(index);
}
else { // insert at the end of the html
intro = intro + '<a class="less" ng-click="this.showFull = false">Less</a>';
}
}
return intro;
};
// Truncate the long intro into a shorter length blurb
this.getShortIntro = function(row, row_index) {
// Truncate if necc.
var short_intro = jQuery.truncate(row['Introduction'], {
length: 250,
words: true,
ellipsis: '\u2026 <a class="more moreish" attr-ng-click="guideList.showFullClick()">Read on</a>'
});
var more = jQuery('.more', short_intro); // select more link
if (more.length) { // do we have a more link
$scope.guideList.rowIndex[row_index] = true;
}
else { // no more link
$scope.guideList.rowIndex[row_index] = false;
}
$compile(short_intro)($scope);
return short_intro;
};
}]);
As you can see in the ViewService.get() call, data is fetched and then processed. The processing simply involves putting a link at the end of the "Intro" field that is intended to be clickable.
For a while I was having a tough time to even get the ng-click directive to even show (it was being filtered out w/out $sce.trustAsHtml). Now it is there but clicking it has no effect.
The main template (from Drupal) currently looks like:
<div class="guide-listing" ng-controller="guideListController as guideList">
<a ng-click="guideList.showFullClick()">Click me</a>
<div class="guide-teaser"
ng-repeat="guide in guideList.guides"
ng-include src="'/sites/all/themes/ngTheme/ngApp/partials/guide_teaser.html'">
<!-- See guide_teaser.html partial for guide teasers -->
</div>
</div>
The ng-click as placed in the Drupal template above works as expected.
And for the partial that is used in the ng-repeat, it looks like so:
<div ng-controller="guideListController as guideList">
<h2 class="guide-teaser-title">{{guide.node_title}}</h2>
<div class="guide-teaser-intro" ng-bind-html="guide.Introduction" ng-show="guide.showFull">
{{guide.Introduction}}
</div>
<div class="guide-teaser-intro-short" ng-bind-html="guide.Intro_short" ng-show="!guide.showFull">
{{guide.Intro_short}}
</div>
</div>
So far I have only been working on getting the ng-click to work on the short_intro and have had no success so far. Any ideas as to what I am doing wrong would be greatly appreciated!
Ok, So I did get some traction! I used the ngHtmlCompile (http://ngmodules.org/modules/ng-html-compile) directive that was created by https://github.com/francisbouvier (thanks!)
The issue was that the new (dynamic) html wasn't being compiled.
At first it didn't work. I had two issues that prevented it from firing:
A: I stopped using $sce.trustAsHtml. Using this in conjunction with the directive caused the content to disappear!
B: The other issue was one of scope. After I changed the directive such that transclude was set to false it worked just fine!

combine string and html string and display in ui-bootstrap popover

I am using restangular to get a collection of items from a server. each item has a description, which is a normal string and a longDescription which contains html describing how it should be formatted.
I am trying to display this information in a single ui-bootstrap popover
I have added a restangule response interceptor like so
var addTrustedDesc = function(room) {
var result;
var desc = room.description;
var longDesc = room.longDescription;
if(desc && longDesc) {
result = desc + ' </br> ' + longDesc;
} else if(desc) {
result = desc;
} else if(longDesc) {
result = longDesc;
}
room.trustedDesc = $sce.trustAsHtml(result);
return room;
};
RestangularConfigurer.addResponseInterceptor(function(data) {
return _.map(data, function(room) {
return addTrustedDesc(room);
});
});
and then much later I try to use the trustedDesc in the popover like so
popover="{{ row.data.trustedDesc }}"
but the popover just displays {}
if I change the line above to
room.trustedDesc = $sce.getTrustedHtml($sce.trustAsHtml(result));
or simply
room.trustedDesc = result;
then the popover displays the full string including the html elements
is it possible to get the popover to render the html and display it?
Your problem is most likely that the bootstrap ui popover cannot handle custom template. I would try to use the custom popover of angular strap instead http://mgcrea.github.io/angular-strap/#popovers

$setPristine() does not work properly if form control has invalid data

I am trying to reset form using $setPristine().
$scope.resetDataEntryForm = function() {
$scope.dataEntryForm.$setPristine();
$scope.pr = {};
};
It works fine if all the input controls in valid state. one of the input type is URL. For example, if i have invalid URL value at the time i click reset. should the above code reset the contents of URL input field and mark error to false. I need this for validations.
To do the proper reset i have to manually reset all error flags
$scope.resetDataEntryForm = function() {
$('#dataEntryForm')[0].reset();
$scope.dataEntryForm.$setPristine();
$scope.dataEntryForm.name.$error.required = true;
$scope.dataEntryForm.site.$error.required = true;
$scope.dataEntryForm.site.$error.url = false;
$scope.pr = {};
};
Can anybody please suggest proper way to reset form using angular.js ?
I do not know if it intended or not, but $setPristine() does not change $error object.
This is the source code for setPristine on Form
form.$setPristine = function () {
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
form.$dirty = false;
form.$pristine = true;
forEach(controls, function(control) {
control.$setPristine();
});
};
and this is the code call on Control (input element etc)
this.$setPristine = function () {
this.$dirty = false;
this.$pristine = true;
$element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
};
Seems to me that you will have to reset $error object yourself
PS: The code is taken from AngularJs version 1.2.*, you can check the current version on Github https://github.com/angular/angular.js/blob/291d7c467fba51a9cb89cbeee62202d51fe64b09/src/ng/directive/form.js

Resources