ng-model preventing ng-value to be displayed - angularjs

I'm pretty new to angular world and I have an issue with it.
I'm working with ejs too.
I have an input that I want to fill (value) with an ng-model.
The problem is my model is empty while the user doesn't specify a value.
I want to display a default value when my model is empty. This default value is sending by the ejs (server side). Doing that, I can't set a default value in my controller.
To do so I wrote the following :
<input type="text" ng-model="owner_adress" ng-value="'{{owner_adress || '<%=user.owner_adress%>'}}'"/>
If I look into my code, I can see the value is okay (ejs result when my model is empty, my model value otherwise) but the value is not displayed in my input (ie the user can't see it).
I looked for a work around (ng-cloak was fine but I can't use it in my input field).
Any clue would be nice !

Use ngInit directive instead. If owner_adress is defined in controller it will be used, otherwise it will default to serverside rendered value:
<input ng-model="owner_adress" type="text"
ng-init="owner_adress = owner_adress || '<%=user.owner_adress%>'"/>

Related

Set input default value and be able to edit it later - AngularJS

I have an input field and I want to set a default value when the user does not write anything.
I read several answers here on Stackoverflow that suggest using ng-init or setting the default value to the scope in the controller.
Both suggestions work to set a default value, but neither of them allow the user to edit the field later. The value is always the default one.
How can I set a default value and be able to edit it later? Thank you!
My input field:
input type="text" ng-model="action.parameters.subject"
You should be able to set an initial value using databinding with the ngModel directive.
In your template:
<input type="text" [ngModel]="test">
In your component file:
test = 'initial value'; // some initial value
The user will be able to change it, but it will initially hold the value of the test variable.

AngularJS : Binding an input with variable made variable undefined

I am new to AngularJS, and I have a problem with ng-model.
Here's my code:
<section class="field row" ng-repeat="field in fields">
<input class="value" ng-show="editMode" placeholder="{{field.name}}" ng-model="field.value" type="url" />
</section>
As you can see, I'm looping through $scope.fields which I got from the server and is an array of about 40 objects that have keys like name and value.
Inside the section I have an input, which has the ng-model property set to field.value. When the server gives a value to the field, it is shown inside the input.
At some point I want to update the user's changes, by sending $scope.fields back to the server.
However, when the user changes something in the inputs, the value for the changes fields becomes undefined.
I hope this describes my problem well enough.
Thanks!
To get the changes, you should pass the original object name i.e fields. Refer below calling fn
ng-click="save(fields)"
The reason this caused a problem is simply because the input was a URL input and I was typing simple "hello" strings to test it, instead of typing URLS. Apparently AngulaeJS only puts the answer in the model if it matches the field type.

How to bind data depending on if checkbox is checked?

Just getting started on Angular, I'm using checkboxes to determine which parameters get sent during a get request (this is my attempt):
<input type="checkbox" ng-click="submit('color[]', 'red')"/>
<input type="checkbox" ng-click="submit('color[]', 'green')" checked/>
<input type="checkbox" ng-click="submit('color[]', 'blue')"/>
I have a variable $scope.params that stores the parameters of my http get request. My submit function is very simple:
$scope.submit = function (attribute, value) {
$scope.params[attribute] = value;
};
It simply adds color[] to the params object. Unlike radio, checkbox values can be stored as arrays and submitted as such: api?color[]=red&color[]=green as my backend is PHP this is the preferred format. However my submit function simply overwrites this every time. I'm not sure how to store multiple params with the same "key".
My other problem is that ng-click is not appropriate for this task as it doesn't take in the current state of the checkbox. Notice that my green checkbox is initially loaded as checked. Is there a way to bind this to my $scope.params object?
Ideally I want to implement something like:
$scope.params = {
"color[]" = ['red', 'green', 'blue']
};
According to Semicolon's answer below, I can use:
<input type="checkbox" ng-model="params.colors[]" ng-true-value="'red'"/>
<input type="checkbox" ng-model="params.colors[]" ng-true-value="'blue'"/>
<input type="checkbox" ng-model="params.colors[]" ng-true-value="'green'"/>
But naming anything with "[]" just breaks the code.
Checkboxes, like all input elements, are supported with ng-model. This core directive is a cousin of ng-bind. It says "the state of this element represents this model". Its behavior is determined by the input type. In the case of a checkbox, the value you are modeling is going to be boolean typically, since a checkbox is fundamentally a "boolean" input (checked/unchecked == true/false).
<input type="checkbox" ng-model="colors.red"/>
In the controller function:
$scope.colors = {
red: false,
green: true,
blue: false
};
Actually you can map checked/unchecked to non-boolean values too, using ng-true-value and ng-false-value. You can read more about these and other options for ng-model with checkboxes in the Angular docs:
https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D
The core difference between ng-bind and ng-model is that the former is unidirectional (just a view) and the latter is bidirectional (the element can be used to change the model).
A more general answer regarding the premise of MVC in Angular:
In the example in your question, you were using a jQuery-like solution to try to achieve binding between the view and controller. This is really not ideal. Only bind a function to click events when you are specifically interested in having something take place "on click".
What if a user uses the tab key and the spacebar to check the box? The model would not get updated.
Or let's say you want to change the value in the model somewhere else -- maybe you have a "reset" button that returns them to the original values. The view would not get updated.
If the connection between the view and the model is all through "actions" it is easy for them to be out of sync. You'd have to make sure you handled every possible way the user could interact with the element and each time you change the data programmatically you would need to push the change to the view explicitly. But using ng-model and ng-bind lets you keep them synced no matter where the model is changed or how the user interacts. Really this is the main point of Angular.

Why directive ng-init doesn't work?

I want to initialize variable test on value from formErrors.field1 but it doesn't work.
{{formErrors.field1}} // show content
<input ng-model="test" ng-init="test = formErrors.field1" /> // input field is empty
{{test}} // doesn't show content
I'm using Angular version angular-1.3.0-rc.1
If the field value gets set after the page loads, maybe it could explain why you face this issue.
I use ng-init mostly to set values directly, not to copy the value of another scope variable.

How to use angularjs form validation in a non-angular SPA app

I wonder if anyone can help. On the surface of it, my question title may sound kind of stupid! I'm trying to use angularjs form validation in a non-angularjs application! Let me try to explain ...
We have a 'traditional' web app. Its not an SPA. The backend is java, and the java app server manages the session and the data within. Each page is a full http request/response. (There are a small number of ajax request/responses, but these are to add some bling to the page, rather than it's core functionality). In this respect, the architecture of the app is very traditional/old-skool, in that the server-side java code is responsible for generating the markup and populating form field values from it's version of the model data held in it's session store. (I think this is the crux of the problem)
The app is predominantly a HTML form based application, and to enhance the UX we have written some javascript field validators based around jQuery. For a number of reasons these have started to get a bit out of control, and we are exploring alternative options.
A simple google search finds countless jQuery plug ins for form validation. We are looking at these, but random jQuery plug ins are not our favoured approach (we tend to steer clear of 'somebloke.com' plug ins because we can't guarantee how well they're written, browser compatibility, future maintenance, how well they work with other plug ins etc - we've had our fingers burnt with this kind of thing before)
So we are looking at other approaches, and are currently exploring the use of angularjs.
Being able to 'gently introduce' angularjs into our architecture has some advantages. It's from a stable best-of-breed organisation (ie. it's not 'somebloke.com') so is well supported and maintained. It encourages us to write our js in a very modular & testable manner (our current rats nest of jQuery plugins, callbacks etc is far from that!). angularjs form validation is based around html5 standards and is declarative and semantic. It gives us a way forward for migrating our other jQuery based code to something better (angularjs directives). And overall, if we can layer angularjs into our current app, it gives us a good foundation for converting the app to a modern SPA at some point in the future.
Rewriting the entire app as an angularjs (or any other mv* framework) SPA at this point in time is not an option, so as mentioned above, we are looking at introducing small bits of functionality at a time; and today's challenge is form validation.
So, that's the background.
I've stripped out our current js client side validation, and our server-side java code is generating markup like this:
<form method="POST" action="/renew">
<input name="firstname" type="text" value="alf" />
<input name="surname" type="text" value="garnet" />
<input name="age" type="number" value="88" />
<input type="submit" />
</form>
(where the values for the input fields have been populated server-side from the model held by the server)
I've added the angularjs library to the page, and have got form validation working as follows:
<form method="POST" action="/renew" novalidate name="renewForm"
ng-controller="yourDetails" ng-submit="submitForm(renewForm, $event)">
<input name="firstname" type="text" value="alf" required ng-model="firstname"/>
<input name="surname" type="text" value="garnet" required ng-model="surname"/>
<input name="age" type="number" value="88" required ng-model="age"/>
<input type="submit" />
</form>
app.controller('yourDetails', function($scope) {
$scope.submitForm = function(form, $event) {
if (!form.$valid) {
$event.preventDefault();
return false;
}
};
});
This is a reasonable starting point. Broadly speaking it works in that angularjs is handling the form validation and submission. The submitForm method is executed, and if the form is not valid then the if block is entered and the form submission is cancelled. From here I can see it would be easy to add in the field error messages etc using ng-show etc.
The problem however is the use of ng-model on each html field. As I understand it I need to use this so that angularjs binds the field to the form, and can therefore track each fields valid status.
However, ng-model also appears to setup the 2-way data binding and sets the value of the field to it's version of the model data ... which is empty. For example:
Our server-side template might contain this:
<input th:field="*{firstname}" type="text" required ng-model="firstname"/>
Which might generate this markup:
<input name="firstname" value="alf" type="text" required ng-model="firstname"/>
The markup that gets served to the client includes value="alf"
But then angularjs steps in and sets up 2-way binding for the field. Because we don't have a firstname property in the angularjs scope, it initialises one with a blank value, and sets that blank value in the DOM of the field.
This results in the page being rendered by the browser with blank values in the fields, even though server-side we have values in the model, and the server has correctly generated the markup etc.
So, I think I understand the core problem and why it's happening. My question is, can I do angularjs form validation without the ng-model attribute on each field, or is there a version of the ng-model directive that only does 1-way binding - specifically DOM -> model
Any help would be very much appreciated;
Thanks
Nathan
When generating your form at server side, you can initialize your model with ng-init:
<input ng-init="firstname='alf'" th:field="*{firstname}" type="text" required ng-model="firstname" />
OK, #Alexandre's answer was almost right, and it was his answer that pointed me in the direction of my final solution (so he should get the credit for this really :) )
ng-init does work as #Alexandre has suggested. The reason I couldn't get it to work was that I was trying to use it on a number field.
The following works because the value being set on the model with ngInit and the html input type are both text/string:
<input ng-init="firstname='alf'" th:field="*{firstname}" type="text" required ng-model="firstname" />
I was trying it on the age field as follows:
<input ng-init="age='88'" th:field="*{age}" type="number" required ng-model="age" />
This does not work because the age property was being set on the model as a string, but the html input type is a number. The following does work:
<input ng-init="age=88" th:field="*{age}" type="number" required ng-model="age" />
This led me to realise that the value being set on the angular model needs to match the data type of the html input type (certainly true of chrome, not sure about other browsers) (ie. string properties in the model - even if they are parse-able as numbers - cannot be used in a html number field with ngModel)
With this in mind, I decided there were 2 options. I could either do it server-side in the code that generates the markup:
<input ng-init="age=(some-potentially-complex-logic-to-workout-whether-its-a-string-or-number)" th:field="*{age}" type="number" required ng-model="age" />
Or I could do it client-side with a custom directive. In the end I went with a custom directive because a) it meant I could have a go at writing a directive (all part of the learning :)) and b) I realised there might be other cases that need special consideration which might make doing it server-side even more complex (ie. select fields don't have a value, they have a selected index of which you need to get it's value; radio buttons all have a value but you only want to set the value of the checked radio)
Here's what I came up with:
angularApp.directive('lvInitializeValueOnScope', function() {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
var propertyName = attrs.name,
propertyValue = attrs.value,
elementName = element.get(0).tagName.toLowerCase(),
fieldType = ( elementName === 'input' ? attrs.type.toLowerCase() : elementName ),
// set expression to assume propertyValue is a string value
expression = propertyName + '=\'' + ( !!propertyValue ? propertyValue : '' ) + '\'';
// if the input field type is number and propertyValue is parse-able as a number
if (fieldType === 'number' && !isNaN(parseFloat(propertyValue))) {
// set expression without quotes surrounding propertyValue
expression = propertyName + '=' + propertyValue;
}
// if the field is a html select element
if (fieldType === 'select') {
// propertyValue will be blank because select elements don't have a value attribute
// instead, we need to use the value of the child option element that is selected
propertyValue = $(element.html()).filter(":selected").val();
// set expression to assume propertyValue is a string value
expression = propertyName + '=\'' + ( !!propertyValue ? propertyValue : '' ) + '\'';
}
// if the input field type is a radio button but its not checked (selected)
if (fieldType === 'radio' && !element.is(':checked')) {
// we need to reset the expression so a blank value is used
// doing this means that only the checked/selected radio button values get set on the model
expression = propertyName + '=\'\'';
}
// evaluate the expression, just as angular's ngInit does
scope.$eval(expression);
}
};
});
(it looks more complex than it actual is because I've left the comments in - strip those out and there's really nothing to it)
To use it, each field that I am using ng-model on, I also need to use the attribute data-lv-initialize-value-on-scope. You don't need to pass any value on the attribute, the directive gets everything it needs from the element and attr parameters
So far it caters with text, number, radio and select fields. The pages I'm working on don't have any checkboxes, but as and when I come across those I dare say I'll need to add some code for those.
It works, and I think I prefer this to using ng-init with a load of server-side to determine whether to set a value, what its data type should be, etc
The only downside I can see at the moment is that processing a radio button set might be inefficient. IE. if you have 10 radio buttons, each marked up with ng-model and data-lv-initialize-value-on-scope; all 10 will run the data-lv-initialize-value-on-scope directive and set a value on the scope. At least 9 of the iterations will set a blank value on the scope, and at most only 1 will set the actual value on the scope.
Hey-ho, it seems to work for now :). Hope this helps someone in the future
Nathan

Resources