Angular lazy one-time binding - dealing with expressions - angularjs

How do I apply one time lazy binding to the following where the value is composed of an expression:
<span ng-bind="::firstname + ' ' + ::lastname"></span> // breaks

Why not composing them in your controller:
$scope.name = $scope.firstname + ' ' + $scope.lastname;
And then simply:
<span ng-bind="::name"></span>

<span ng-bind="::(firstname + ' ' + lastname)"></span>
Parens aren't necessary though.

Related

angular-formly validation messages translation using angular-translate

I am trying to translate formlyjs required validation message in angularjs that's the code what I have tried but it doesn't translate or bring the translated string in "REQUIRED" I am using angular-translate.
Output i am getting UsernameREQUIRED instead of Username is required
I have looked at this question and answer over stackoverflow. it works with out to.label but i need to have this to.label concatenation.
Any help or suggestion please.
$translateProvider.translations("en", {
REQUIRED: "is required",
})
formlyValidationMessages.messages.required = 'to.label + "REQUIRED" | translate';
angular.module('templates/formly/validation-messages.html', []).run([
'$templateCache',
function ($templateCache) {
$templateCache.put('templates/formly/validation-messages.html',
'<formly-transclude></formly-transclude>' +
'<div class="validation-messages" ng-messages="(fc[0] || fc).$error" ng-if="(options.formControl[0] || options.formControl).$touched || (options.formControl[0] || options.formControl).$error.required || form.$submitted" ng-messages-multiple>' +
' <div class="label label-danger animate vanishIn enter-vanishIn exit-vanishOut" ng-message="{{::name}}" ng-repeat="(name, message) in ::options.validation.messages">' +
' {{message((fc[0] || fc).$viewValue, (fc[0] || fc).$modelValue, this)}}' +
' </div>' +
'</div>' +
'');
}
]);

Not able to get ng-model value from dynamically created input box

Here is how I am adding fields.
$scope.addEmailField = function () { //Function to add new email field.
if (valid <= 1 && checkToDelete == 0) {
var mailTxtId = 'mail' + valid;
var mailModel = 'Contact_Email' + valid;
var hide = 'hide' + valid;
hide = false;
console.log(mailTxtId);
var emailDiv = angular.element(document.querySelector('#emailDiv'));
var element = $compile('<div id="' + mailTxtId + '" style="margin-left: -60px; width:200px; margin-top:15px"><input id= "' + mailModel + '" type = "text" class="form-control" ng-model="' + mailModel + '" ng-blur="validateEmailDynamic(' + valid + ')">' +
'<input id="' + valid + '" class="form-control" style="margin-left: 206px; width:54px; margin-top:-34px" type="button" value="-" ng-click="deleteField(' + valid + ')"><span ng-show ="' + hide + '" style="color:red">' +
'Invalid email</span></div>')($scope);
emailDiv.append(element);
valid = valid + 1;
}
};
But not getting the value of ng-model.
Store your input box in a directive's template. Then add ng-class that would determine whether it should show or not.
app.directive('inputBox', function(){
template:'<input ng-model="item">'
});
Usage in the html:
<div ng-class="{ input-box : triggerInputBox }"></div>
Controller:
$scope.triggerInputBox = true;
This is just one of many ways to accomplish this. But directives are very useful for dynamically showing templates.

AngularJS 1.6.x Dynamically Load Components with html string and ng-if="{{ }}"

I am having an issue dynamically loading components with {{}} using Angular 1.6.x
I can load the components dynamically just fine using compile, but the issue I am facing is adding an ng-if={{}} to the html string.
If I go this route it will take what ever vm.page is set to at the time. i.e. 1:
for (var i = 0; i < vm.wizardPages.length; i++) {
var newScope = $scope.$new(true, $scope);
newScope = angular.merge(newScope, vm.wizardPages[i]);
var html = '<' + vm.wizardPages[i].componentName + ' ng-if="' +
vm.page + ' === ' + (i + 1) + '"></' + vm.wizardPages[i].componentName
+ '>';
var element = $('page');
element.append($compile(html)(newScope));
}
Above renders:
<service-center-branch-selection ng-if="1 === 1" class="ng-scope ng-isolate-scope">
...
</service-center-branch-selection>
How can I call compile with {{}} in the string so vm.page is using data binding and can change when vm.page changes value?:
// loop through the data and inject components
for (var i = 0; i < vm.wizardPages.length; i++) {
var newScope = $scope.$new(true, $scope);
newScope = angular.merge(newScope, vm.wizardPages[i]);
var html = '<' + vm.wizardPages[i].componentName + ' ng-if="{{vm.page}} === ' + (i + 1) + '"></' + vm.wizardPages[i].componentName + '>';
var element = $('page');
element.append($compile(html)(newScope));
console.log(newScope);
}
I want the above to work with:
<service-center-branch-selection ng-if="vm.page === 1" class="ng-scope ng-isolate-scope">
...
</service-center-branch-selection>
Changed to "currentPage":
// loop through the data and inject components
for (var i = 0; i < vm.wizardPages.length; i++) {
var newScope = $scope.$new(true, $scope);
newScope = angular.merge(newScope, vm.wizardPages[i]);
var html = '<' + vm.wizardPages[i].componentName + ' ng-if="currentPage === ' + (i + 1) + '"></' + vm.wizardPages[i].componentName + '>';
var element = $('page');
element.append($compile(html)(newScope));
console.log(newScope);
}
Add 'currentPage' binding to component so available for new scope:
app.component("wizard", {
template: require('./wizard.component.html'),
controllerAs: "vm",
controller: wizardController,
bindings: {
breadcrumbs: '<',
wizardPages: '<',
currentPage: '<'
}
});
Add variable input to markup
<wizard breadcrumbs="vm.breadcrumbs" wizard-pages="vm.wizardPages" current-page="vm.page">
...
</wizard>
This is untested, but hopefully gives you the idea. Also, if the isolate scope has the ability to change the current page then you will need to update it to a two-way binding of course.

AngularJS, Regex causes error in test

When the user enters a specific expression, it gets translated to a button in the view.
Here is the code:
scope.buttonmaker = function(haystack) {
needle = /argumentation-link_to\((\d+),\s([\w\sÀ-ž]+)\)/;
return $sce.trustAsHtml(haystack.replace(new RegExp(needle, 'gi'), function(match) {
return '<button ng-click="goToArgumentation(' + needle.exec(match)[1] + ', 2, 3, false)" class="btn btn-md btn-info"> ' + needle.exec(match)[2] + '</button>'
}));
};
The code works, as it should and it also captures more than one expression, that match the Regex and the buttons are made correctly.
However, when running a test, this error happens:
Error: Cannot supply flags when constructing one RegExp from another.
RegExp#[native code]
buttonmaker#http://127.0.0.1:46071/assets/application-a2e90460a4bfb22fd82a11fde4c3041112d7349933767571c5e4928ad1951bdb.js:56321:68
fn
Looking at this stackoverflow-question, I first thought, that I did make a spelling mistake, so I tried this:
needle = "argumentation-link_to\((\d+),\s([\w\sÀ-ž]+)\)";
No errors occur, but the buttons weren't made.
Then, I tried this:
needle = /argumentation-link_to\((\d+),\s([\w\sÀ-ž]+)\)/gi
return $sce.trustAsHtml(haystack.replace(needle, function(match) {
return '<button ng-click="goToArgumentation(' + needle.exec(match)[1] + ', 2, 3, false)" class="btn btn-md btn-info"> ' + needle.exec(match)[2] + '</button>'
}));
This error occurs:
Error: needle.exec(...) is null
After looking at this stackoverflow-question, I believe, I have to somehow iterate over my text, but I don't know how to do it in my case.
Can someone help me to correct the code, so it does work in tests too?
EDIT:
When I do this:
needle = /argumentation-link_to\((\d+),\s([\w\sÀ-ž]+)\)/;
return $sce.trustAsHtml(haystack.replace(needle, function(match) {
return '<button ng-click="goToArgumentation(' + needle.exec(match)[1] + ', 2, 3, false)" class="btn btn-md btn-info"> ' + needle.exec(match)[2] + '</button>'
}));
No errors occur, but ONLY the first button gets made. This is obvious, because the global flag is missing.
And since the code works outside of tests, I think, the problem is more about the flags than the constructor.
I found a cheap fix, which requires not too much maintenance-work:
needle = /argumentation-link_to\((\d+),\s([\w\sÀ-ž]+)\)/gi
needle2 = /argumentation-link_to\((\d+),\s([\w\sÀ-ž]+)\)/
return $sce.trustAsHtml(haystack.replace(needle, function(match) {
return '<button ng-click="goToArgumentation(' + needle2.exec(match)[1] + ', 2, 3, false)" class="btn btn-md btn-info"> ' + needle2.exec(match)[2] + '</button>'
}));

angular two way dymanic binding issue

Im trying to output the following info and have it update realtime using angulars automatic binding. the first and last update ok, but full isnt updating as I would expect. im grateful for any assistance.
http://jsfiddle.net/laurencefass/74u313gx/1/
required output:
first = john
last = doe
full name=john doe.
HTML
<div ng-app="nameApp">
<div ng-controller="nameController">
<input ng-model="first"/>
<p>first = {{first}}</p>
<input ng-model="last"/>
<p>last = {{last}}</p>
<p>full name = {{full}}</p>
</div>
JS
var app=angular.module("nameApp", []);
app.controller('nameController', function($scope) {
$scope.first="john";
$scope.last="doe";
$scope.full = $scope.first + " " + $scope.last;
});
initial output seems correct and the first and last update as expected. but the full name is not updating despite being a $scope var and a product of first and last.
The following line is only run once. So it is initiated with the first value that has been assigned to first and last.
$scope.full = $scope.first + " " + $scope.last;
So, if you want your binding to work, without having a unnecessary fonction into your controller. (keep your controllers as clean as possible!)
<div ng-app="nameApp">
<div ng-controller="nameController">
<input ng-model="first"/>
<p>first = {{first}}</p>
<input ng-model="last"/>
<p>last = {{last}}</p>
<p>full name = {{first + ' ' + last}}</p>
</div>
</div>
Have a look at your fiddle :
http://jsfiddle.net/74u313gx/2/
If you really need to have the fullname into the controller, you can use a $watch therefor :
$scope.$watch('first', updateFull);
$scope.$watch('last', updateFull);
function updateFull(){
$scope.full = $scope.first + " " + $scope.last;
}
If you are concerned by performance, you may want to avoid defining to much watches, then a ng-change can be used :
controller :
$scope.updateFull = function(){
$scope.full = $scope.first + " " + $scope.last;
}
View :
<div ng-app="nameApp">
<div ng-controller="nameController">
<input ng-model="first" ng-change="updateFull();"/>
<p>first = {{first}}</p>
<input ng-model="last" ng-change="updateFull();"/>
<p>last = {{last}}</p>
<p>full name = {{full}}</p>
</div>
Since you're defining a string on $scope that is concatenated with several parameters, it won't change automatically when you change the parts that you used to assemble it with.
If you want to achieve what you're looking for, you can do one of two things:
One:
<p>full name = {{first + ' ' + last}}</p>
Two:
<p>full name = {{getFullName()}}</p>
And in the controller have a function:
$scope.getFullName = function () {
return $scope.first + ' ' + $scope.last;
}
it will not update auto since your are passing a string in full name after concatenation it returns simple string not an angular var, you need to do the following to update value automatically.
$scope.$watchGroup(["first","last"],function(){
$scope.full = $scope.first + " " + $scope.last;
});
update your code like below:
View Update
<div ng-app="nameApp">
<div ng-controller="nameController">
<input ng-model="first" ng-change="change()"/>
<p>first = {{first}}</p>
<input ng-model="last" ng-change="change()" />
<p>last = {{last}}</p>
<p>full name = {{full}}</p>
Script Update
var app=angular.module("nameApp", []);
app.controller('nameController', function($scope) {
$scope.first="john";
$scope.last="doe";
$scope.full = $scope.first + " " + $scope.last;
$scope.change= function(){
$scope.full=$scope.first + " " + $scope.last;
}
});
As you seen above, I am changed/update view page input elements with ng-change directive and create same scope function to namecontroller w.r.t.nameApp Module.
Hope its helps you!!!

Resources