How to get the value from ACE-Angular editor - angularjs

I am having problems on getting Ace code editor results from whatever I type on the code editor.
As you can see I set up my controller with all the functionalities
and options. The only thing that is not working is rendering the results from editor. Thank you in advance....
app.controller('AceCtrl', function ($scope) {
$scope.modes = ['CoffeeScript'];
$scope.mode = $scope.modes[0];
$scope.aceOption = {
mode: $scope.mode.toLowerCase(),
onLoad: function (editor) {
// defaults
editor.setTheme("ace/theme/monokai");
// options
editor.setOptions({
showGutter: true,
showPrintMargin: false,
});
$scope.modeChanged = function () {
editor.getSession().setMode("ace/mode/" + $scope.mode.toLowerCase());
};
}
};
//Runs every time the value of the editor is changed
$scope.aceChanged = function(_editor){
console.log('Ace editor changed');
// Get Current Value
var currentValue = _editor.getSession().getValue();
// Set value
_editor.getSession().setValue('This text is now in the editor');
};
});
<div class="wrapper" ng-controller="AceCtrl">
<section >
<select ng-model="mode" ng-options="m for m in modes" ng-change="modeChanged()" autofocus="0"></select>
<div ui-ace="aceOption" ng-model="aceModel" class="ace_editor"></div>
</section>
<div ng-change="aceChanged" style="border:1px solid red; position:relative; width:50%; height:300px; float:right;"></div>
</div>

If you want to do this in the onChange handler of ui-ace you could get the document from the editor's session and grab it's value:
editor.getSession().getDocument().getValue();
Reference: http://ajaxorg.github.io/ace/#nav=api&api=document
Example code:
<div ui-ace="{
onLoad: aceLoaded,
onChange: aceChanged
}"></div>
<textarea id="ace_document">
{{aceDocumentValue}}
</textarea>
$scope.aceLoaded = function(_editor) {
$scope.aceSession = _editor.getSession();
};
$scope.aceChanged = function () {
$scope.aceDocumentValue = $scope.aceSession.getDocument().getValue();
};
Working example on Plunker: http://plnkr.co/edit/9swlVY9JnIfOnbOAfXui?p=preview
But the easiest way is to assign the value to scope and use ng-model on the directive's element:
Controller:
angular.module('app').controller('rootController', [
'$scope',
function ($scope) {
$scope.aceValue = 'Foobar';
}
]);
Template:
<div ui-ace ng-model="aceValue"></div>
<textarea id="ace_document" ng-model="aceValue"></textarea>
Working example on Plunker: http://plnkr.co/edit/fbtzYDEqIQEwB6Ascm3s?p=preview

Related

Unable to Set Angular Model from Javascript

I tried to set an angularjs model value from a Javascript code but it doesn't work. I always get an empty value for that property. Below is a snippet of my html code:
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformScripts: true,
usePlatformStyles: true
});
VSS.ready(function () {
console.log("Organization name " + VSS.getWebContext().account.name);
var scope = angular.element(document.querySelector('#hubContainer')).scope();
scope.$apply(function () {
scope.DashboardModel.AccountKey = VSS.getWebContext().account.name;
})
});
</script>
<div class="ng-cloak tree-master-wrapper">
<div id="hubContainer" class="ng-scope" data-ng-controller="MyController">
Some code comes here…….
</div>
But for some reasons the console.log(“AccountKey “ + $scope.DashboardModel.AccountKey) is empty. Any idea? I using https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js
I was able to resolve this by adding this entry to "MyController":
MyModule.controller('MyController', ['$scope', function($scope){
$scope.DashboardModel = new DashboardModel();
var checkPermission = function (selectedTeam) {
VSS.require(["VSS/Service", "VSS/Security/RestClient"],
function(VSS_Service, Security_RestClient) {
$scope.DashboardModel.AccountKey = VSS.getWebContext().account.name;
});
});
}

Custom directive dom not change when parent scope make changes using {{}} with # attribute

I am creating a custom directive with isolate scope using interpolation ({{}}) from parent scope, should be when parent scope is change the attribute should be updated with the new data. i have only 1 data been changed, the other is not change.
i dont need 2 way binding just 1 way binding is enough that is why i am using # as an attribute property.
my parent html
<button ng-click="testClick()">Test Click</button>
<my-directive ng-repeat="sensor in sensors track by sensor.sensor_name"
sensor-name="{{ sensor.sensor_name }}" display-name="{{sensor.display_name}}"
state-normal="{{ sensor.stateNormal }}" state-alert="{{ sensor.stateAlert }}"
state-total="{{ sensor.total }}"></my-directive>
my directive template
<div>
<span>{{ displayName }}</span>
</div>
<div>
Normal
</div>
<div>
{{ states["Normal"] }}
</div>
<div>
Alert
</div>
<div>
{{ states["Alert"] }}
</div>
<div>
Total
</div>
<div>
{{ states["Total"] }}
</div>
inside my parent scope
$scope.sensors = [{
sensor_name: "stre",
display_name: "Stre"
}];
var initState = {
normal: "0",
alert: "0"
};
var setInitState = function(sensors) {
for (let i = 0; i < sensors.length; i++) {
sensors[i]["stateNormal"] = "0";
sensors[i]["stateAlert"] = "0";
sensors[i]["total"] = "0";
}
return sensors;
}
$scope.sensors = setInitState($scope.sensors);
$scope.testClick = function() {
$scope.sensors[0].display_name = "testchange";
$scope.sensors[0].stateNormal = "15";
$scope.sensors[0].total = "38";
}
my directive scope
app.directive("myDirective", function() {
return {
restrict: 'EAC',
controller: function($scope) {
$scope.states = {
"Normal": $scope.stateNormal ? $scope.stateNormal : 'x',
"Alert": $scope.stateAlert ? $scope.stateAlert : 'x',
"Total": $scope.stateTotal ? $scope.stateTotal : 'x'
};
},
templateUrl: "my-directive.php",
scope: {
sensorName: '#',
displayName: '#',
stateNormal: '#',
stateAlert: '#',
stateTotal: '#'
}
};
});
the button click is expecting changes towards all the value, but when the button click only the display_name is change but normal and total value is not changing.
you can refer to this plunkr: https://embed.plnkr.co/aXctKP/
you can check out this working plunker.
You can check out angularjs docs here to better understand how directive work.
What I do to make it right is I rename the variable inside my-directive.php to follow the attribute you have set in the index.html. You can read the angularjs doc under the Normalization section, it says that it will normalize the element's attribute from state-total to stateTotal.

Pitfalls of the New AngularJS ng-ref Directive

The release of AngularJS V1.7.1* introduces the new ng-ref directive. While this new directive enables users to easily do certain things, I see great potential for abuse and problems.
The ng-ref attribute tells AngularJS to publish the controller of a component on the current scope. This is useful for having a component such as an audio player expose its API to sibling components. Its play and stop controls can be easily accessed.
The first problem is the player controls are undefined inside the $onInit function of the controller.
Initial vm.pl = undefined <<<< UNDEFINED
Sample = [true,false]
For code that depends on data being available, how do we fix this?
The DEMO
angular.module("app",[])
.controller("ctrl", class ctrl {
constructor() {
console.log("construct")
}
$onInit() {
console.log("onInit", this.pl);
this.initPL = this.pl || 'undefined';
this.sample = this.pl || 'undefined';
this.getSample = () => {
this.sample = `[${this.pl.box1},${this.pl.box2}]`;
}
}
})
.component("player", {
template: `
<fieldset>
$ctrl.box1={{$ctrl.box1}}<br>
$ctrl.box2={{$ctrl.box2}}<br>
<h3>Player</h3>
</fieldset>
`,
controller: class player {
constructor() {
console.log("player",this);
}
$onInit() {
console.log("pl.init", this)
this.box1 = true;
this.box2 = false;
}
},
})
<script src="//unpkg.com/angular#1.7.1/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as vm">
Initial vm.pl = {{vm.initPL}}<br>
Sample = {{vm.sample}}<br>
<button ng-click="vm.getSample()">Get Sample</button>
<br>
<input type="checkbox" ng-model="vm.pl.box1" />
Box1 pl.box1={{vm.pl.box1}}<br>
<input type="checkbox" ng-model="vm.pl.box2" />
Box2 pl.box2={{vm.pl.box2}}<br>
<br>
<player ng-ref="vm.pl"></player>
</body>
Getting ref to components controller isn't new, directives back in the day allowed it and that wasn't a problem at all, it's necessary to have such feature, ng-ref is just a helper for you to do this from the template side (the same way angular 2+ does).
Nevertheless, if you need the child components ready you should use $postLink() instead of $onInit. $postLink is called after the component is linked with his children, which means, the ng-ref will be ready when it gets called.
So all you have to do is change your onInit like so:
̶$̶o̶n̶I̶n̶i̶t̶(̶)̶ ̶{̶
$postLink() {
console.log("onInit", this.pl);
this.initPL = this.pl || 'undefined';
this.sample = this.pl || 'undefined';
this.getSample = () => {
this.sample = `[${this.pl.box1},${this.pl.box2}]`;
}
}
$postLink() - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation. Note that child elements that contain templateUrl directives will not have been compiled and linked since they are waiting for their template to load asynchronously and their own compilation and linking has been suspended until that occurs. This hook can be considered analogous to the ngAfterViewInit and ngAfterContentInit hooks in Angular. Since the compilation process is rather different in AngularJS there is no direct mapping and care should be taken when upgrading.
Ref.: Understanding Components
The full working snippet can be found bellow (I removed all console.log to make it clearer):
angular.module("app",[])
.controller("ctrl", class ctrl {
constructor() {
//console.log("construct")
}
$postLink() {
//console.log("onInit", this.pl);
this.initPL = this.pl || 'undefined';
this.sample = this.pl || 'undefined';
this.getSample = () => {
this.sample = `[${this.pl.box1},${this.pl.box2}]`;
}
}
})
.component("player", {
template: `
<fieldset>
$ctrl.box1={{$ctrl.box1}}<br>
$ctrl.box2={{$ctrl.box2}}<br>
</fieldset>
`,
controller: class player {
constructor() {
//console.log("player",this);
}
$onInit() {
//console.log("pl.init", this)
this.box1 = true;
this.box2 = false;
}
},
})
<script src="//unpkg.com/angular#1.7.1/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as vm">
Initial vm.pl = {{vm.initPL}}<br>
Sample = {{vm.sample}}<br>
<button ng-click="vm.getSample()">Get Sample</button>
<br>
<input type="checkbox" ng-model="vm.pl.box1" />
Box1 pl.box1={{vm.pl.box1}}<br>
<input type="checkbox" ng-model="vm.pl.box2" />
Box2 pl.box2={{vm.pl.box2}}<br>
<player ng-ref="vm.pl"></player>
</body>
Parent controller initialization happens before the initialization of the player controller, so that is why we have initPL as undefined in the first $onInit.
Personally, I would prefer to define and load the data that should be passed down to the nested components on the parent controller initialization instead of setting the parent's initial data from its child's. But still if we will need this we can do it on the child's component initialization using bindings and callbacks. Probably it looks more like a dirty workaround, but it could work in such scenarios, here is the code:
angular.module("app",[])
.controller("ctrl", class ctrl {
constructor() {
console.log("construct")
}
$onInit() {
console.log("onInit", this.pl);
this.getSample = () => {
this.sample = `[${this.pl.box1},${this.pl.box2}]`;
}
this.onPlayerInit = (pl) => {
console.log("onPlayerInit", pl);
this.initPL = pl || 'undefined';
this.sample = `[${pl.box1},${pl.box2}]`;
}
}
})
.component("player", {
bindings: {
onInit: '&'
},
template: `
<fieldset>
$ctrl.box1={{$ctrl.box1}}<br>
$ctrl.box2={{$ctrl.box2}}<br>
<h3>Player</h3>
</fieldset>
`,
controller: class player {
constructor() {
console.log("player",this);
}
$onInit() {
console.log("pl.init", this)
this.box1 = true;
this.box2 = false;
if (angular.isFunction( this.onInit() )) {
this.onInit()(this);
}
}
},
})
<script src="//unpkg.com/angular#1.7.1/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as vm">
Initial vm.pl = {{vm.initPL}}<br>
Sample = {{vm.sample}}<br>
<button ng-click="vm.getSample()">Get Sample</button>
<br>
<input type="checkbox" ng-model="vm.pl.box1" />
Box1 pl.box1={{vm.pl.box1}}<br>
<input type="checkbox" ng-model="vm.pl.box2" />
Box2 pl.box2={{vm.pl.box2}}<br>
<br>
<player ng-ref="vm.pl" on-init="vm.onPlayerInit"></player>
</body>

how to watch changes on hidden fields with Angular?

I have dropdown made from divs. It's not my doing, is from external agency. But this is not important. The div with a dropdown looks like this:
<label class='label-block' for='kraj-dokument'>Kraj wydania dokumentu tożsamości *</label>
<input type='hidden' name='kraj-dokument' id='kraj-dokument' value="" drop-down-validation-directive /> <div class="select kraj-dokument" data-destination="kraj-dokument">
<p class="label">Wybierz z listy</p>
<div class="options">
<p class="option" data-ng-repeat="country in countries">{{country.Name}}</p>
</div>
</div>
When you click on a choice in this dropdown an external javascript is adding th the hidden field attribute called value and it insert a text from the choise to that value. As you can see I have a directive on that hidden inputu which looks like this (after several searches, and everything):
myapp.directive("dropDownValidationDirective", function () {
return function(scope, elem, attr, ctrl) {
scope.$watch(attr['value'], function(nv) {
elem.val(nv);
}
);
}
});
The problem is when I'm doing watch nothing happens, the value is not geting watched although the change is seen when debuging this in chrome. By the way. I'm trying to also do a validation on this dropdowwn. My idea was to check if this value is filled or not and tell to the user to fill the dropdown by adding a class to it wich marks this thing red. Is that the way to do it?
UPDATE I forgot to add, and I think this is also important that the click event on that div is done with the mentioned external javascript. I'm pasting it below. I've put the external javascript functionality into the service:
myApp.service('DropDownService', function () {
this.renderDropDown = function () {
function initEvents() {
var selectClicked = $(".selectClicked");
$(".select").each(function () {
var destination = $(this).attr("data-destination");
var option = $(this).find(".option");
var options = $(this).find(".options");
var label = $(this).find(".label");
var select = $(this);
label.click(function () {
if (label.hasClass("clicked")) {
$(".select .options").hide();
$(".select .label").removeClass("clicked");
$(".select").removeClass("clicked");
selectClicked.removeClass("clicked");
} else {
$(".select .label").removeClass("clicked");
$(".select").removeClass("clicked");
label.addClass("clicked");
select.addClass("clicked");
selectClicked.addClass("clicked");
$(".select .options").hide();
options.show();
}
});
option.unbind("click").bind("click", function () {
$("#" + destination).attr("value", $(this).text());
label.text($(this).text());
options.hide();
$(".select .label").removeClass("clicked");
$(".select").removeClass("clicked");
$(".select").removeClass("error");
selectClicked.removeClass("clicked");
});
});
}
angular.element(document).ready(function () {
initEvents();
if (navigator.appVersion.indexOf("Mac") !== -1) {
$('body').addClass("MacOS");
}
});
}
Just use interpolation & attrs.$observe instead:
HTML:
<div ng-controller="AppController">
<label class="label-block" for="kraj-dokument">Kraj wydania dokumentu tożsamości *</label>
<input type="hidden" name="kraj-dokument" id="kraj-dokument" value="{{ TotalPrice }}" drop-down-validation-directive />
<div class="select kraj-dokument" data-destination="kraj-dokument" ng-click="AddItem()">
Click to change and see console output
</div>
</div>
JS:
var app = angular.module('my-app', [], function() {})
app.controller('AppController', function($scope) {
$scope.Quantity = 0;
$scope.TotalPrice = 0;
$scope.Price = 100;
$scope.AddItem = function() {
$scope.Quantity++;
$scope.TotalPrice = $scope.Price * $scope.Quantity;
};
});
app.directive("dropDownValidationDirective", function() {
return {
link: function(scope, elem, attrs, ctrl) {
attrs.$observe('value', function(nv) {
console.log(nv);
});
}
}
});
Working example: http://jsfiddle.net/ghd9c8q3/56/
I'm not sure the way you're doing things is the best way, but I know why your watch doesn't do anything.
$scope.$watch() expects either a function, or an expression.
If you pass a function, it's called at each digest loop and its result is the value passed to the change listener.
If you pass an expression, it's evaluated on the scope, and the result of the evaluation is the value passed to the change listener.
So what you actually need is:
scope.$watch(function() {
return attr['value'];
}, function(nv) {
elem.val(nv);
});

Angularjs toggle image onclick

I'm trying to toggle a button image when a user clicks it. I prefer to use angularjs instead of jquery if possible. Right now I have a working version which toggles the image when clicked, the only problem is it changes ALL the images on click. How do I reduce the scope or pass in the src attribute for the img element?
<div ng-repeat="merchant in merchants">
<div class="followrow">
<a ng-click="toggleImage()"><img id="followbutton" ng-src="{{followBtnImgUrl}}" /></a>
</div>
</div>
app.controller('FollowCtrl', function CouponCtrl($scope) {
$scope.followBtnImgUrl = '/img1'
$scope.toggleImage = function () {
if ($scope.followBtnImgUrl === '/img1.jpg') {
$scope.followBtnImgUrl = baseUrl + '/img2.jpg';
} else {
$scope.followBtnImgUrl = 'img1.jpg';
}
}
});
Can I pass in the img src attribute the function like toggleImage(this.img.src) or similar?
<div ng-repeat="merchant in merchants">
<div class="followrow">
<a ng-click="toggleImage(merchant)"><img id="followbutton" ng-src="{{merchant.imgUrl}}" />
</a>
</div>
</div>
.
app.controller('FollowCtrl', function CouponCtrl($scope) {
$scope.followBtnImgUrl = '/sth.jpg'
$scope.merchants = [{imgUrl: "img1.jpg", name:"sdf"},
{imgUrl: "img2.jpg", name: "dfsd"}];
$scope.toggleImage = function(merchant) {
if(merchant.imgUrl === $scope.followBtnImgUrl) {
merchant.imgUrl = merchant.$backupUrl;
} else {
merchant.$backupUrl = merchant.imgUrl;
merchant.imgUrl = $scope.followBtnImgUrl;
}
};
});
What you want is a new scope for each followrow. As your code stands, there's only one scope that all of the followrows are referencing.
A simple answer is to create a new controller that you attach to each followrow:
<div class="followrow" ng-controller="ImageToggleCtrl">...</div>
And then move the image toggling logic to that new controller:
app.controller('ImageToggleCtrl', function($scope) {
$scope.followBtnImgUrl = '/img1';
$scope.toggleImage = function() { /* the toggling logic */ };
});
Now, a new scope will be instantiated for each row, and the images will toggle independently.
I just added two clickable images:
<div ng-app="FormApp" ng-controller="myController" max-width="1150px;" width="1150px;" >
<input ng-show="ShowDown" type="image" style="width:250px; height:40px;" src="~/Content/Images/contactShow.png" ng-click="ShowHide()"/>
<input ng-show="ShowUp" type="image" style="width:250px; height:40px;" src="~/Content/Images/contactHide.png" ng-click="ShowHide()" />
</div>
They toggle eachothers visibility. At page load one is visible, one is not, and both clickable images call the same function:
<script type="text/javascript">
var app = angular.module('FormApp', [])
app.controller('myController', function ($scope) {
$scope.ShowDown = true;
$scope.ShowUp = false;
$scope.ShowHide = function () {
$scope.ShowDown = $scope.ShowDown ? false : true;
$scope.ShowUp = $scope.ShowUp ? false : true;
}
});
</script>

Resources