Why AngularJS expression is replaced only once in HTML template? - javascript

I am new to angularjs and trying following html template. But angularjs only replace the first {{ fragment.id }} expression in text area with the value. All the other {{ fragment.id }} expressions are left unmodified in generated html code.
<div class="input-box" ng-repeat="fragment in project.fragments">
<textarea id="code-{{ fragment.id }}" class="code-input" ng-model="content">{{ fragment.content }}</textarea>
<button class="btn btn-xs btn-primary" style="margin-top:5px;" ng-click="execute({{ fragment.id }})"><i class="glyphicon glyphicon-play"></i> Run</button>
<script type="text/javascript">
applyCodeMirror({{ fragment.id }});
</script>
<div id="result-{{ fragment.id }}" ng-model="result"></div>
</div>
I also have a another question is it possible to use angularjs expression to generate JavaScript code like I have done above.

A few things (the code above is concerning)
Use ng-model to bind $scope variables to inputs - your textarea becomes: <textarea id="code-{{ fragment.id }}" class="code-input" ng-model="fragment.content"></textarea>
{{}} is used to output Angular $scope variables to the view - you would not use {{}} in JavaScript code to get Angular variables, so the <script>{{fragment.id}}</script> is invalid. If you want to use the Angular $scope variables outside Angular (not recommended, but we all have our reasons) - use angular.element(elem).scope() to get an instance of the scope (where elem is an element within the ng-controller declaration)
Angular directives (like your ng-click) do not need {{}} to get $scope variables - your ng-click becomes: ng-click="execute(fragment.id)"

Related

Using a search feature in angular js

I am trying to use the search feature in AngularJS (in Ionic framework). I have a template consisting of different views and a common header for all views. On each view, a different list appears using JSON. I want to use the search feature for each view.
<form class="form-inline">
<div class="form-group">
<input type="text" ng-model="search" class="form-control" placeholder="Search">
</div>
</form>
<ion-item class="item-button-right item-avatar item-icon-right item item-thumbnail-left" ng-repeat="text in texts|filter:search" type="item-text-wrap" href="#/otherContent/texts/{{text.id}}">
This code works fine if I use the above search form and ion-item in one single view. But does not work if I add search form in searchbar and put <ion-item> tag in the different page.
I hope this makes sense? Can anyone please suggest how can we implement this. I need searchbar in the header which is common for all views and it should search for each view when it is loaded. Any pointers are truly appreciated.
If you have a top level controller which is added to like html or body tag (if not then define a top level controller like GlobalController and add it to your <html> or <body> tag so that we can keep the global data there and it's scope can be globally accessible app-wide. Basically we are trying to avoid the $rootScope usage and mimicking the $rootScope usage.), then define an empty object in that global controller:
$scope.globalModel = {};
Then, change your ng-model like:
<input type="text" ng-model="globalModel.search" class="form-control" placeholder="Search">
And finally modify your search filter like:
<ion-item class="item-button-right item-avatar item-icon-right item item-thumbnail-left" ng-repeat="text in texts|filter:globalModel.search" type="item-text-wrap" href="#/otherContent/texts/{{text.id}}">
The above should work. What we are doing here is that defining an object in a parent scope so that we can prevent the problem of Angular Scopes Inheritance.

AngularJS ngMessages can't bind to $index expression

I am building an Angular form that needs repeatable form elements inside an ngRepeat.
<form name="form">
<div ng-repeat="x in [1,2,3,4]">
<input name="something_{{$index}}" ng-model="hi" required>
<div ng-messages="form.something_{{$index}}.$error">
<ng-message="required">This is required</ng-message>
</div>
</div>
<pre>{{form | json: 4}}</pre>
</form>
Angular now supports dynamically declared input names so that you don't have to do something like:
<div ng-repeat="x in [1,2,3,4] ng-form="repeated-form"></div>
And you can use {{$index}} inside the ngRepeat to declare items dynamically. But this doesn't seem to work with ngMessages, which throws an error when I try to bind the index into it.
i.e. this:
<div ng-messages="form.something_{{$index}}.$error">
throws this:
Error: [$parse:syntax] Syntax Error: Token '{' is an unexpected token at column 16 of the expression [form.something_{{$index}}.$error] starting at [{{$index}}.$error].
How can we dynamically declare which property on the form to watch, if ng-messages can't watch the form value that is declared with its {{$index}}?
PLNKR: http://plnkr.co/edit/4oOasbtffTgKqmxcppUG?p=preview (check console)
ng-messages="form['something_' + $index].$error"
Should work. I generally wouldn't put {{ }} in any of the ng directives because most of the ng directives execute with priority level 0 (including the {{ }} directive, ngBind). Also, the ng directives all use $evaluate on their argument, so they look at variable values in the scope by default.
Priority 0 for multiple directives on the same element means that Angular can't guarantee which directive will be applied first. Because of that, it is generally best to avoid using ngDirectives together as behavior can vary. ngIf is an exception as it executes with priority 600 (which is why directives aren't evaluated for an ng-if element not currently in the DOM no matter what).
<div ng-repeat="x in [0,1,2,3]">
<input name="something_{{$index}}" ng-model="hi" required>
<div ng-messages="form['something_' + $index].$error">
<ng-message="required">This is required</ng-message>
</div>
</div>
http://plnkr.co/edit/k5nzkpkJwSuf5dvlMMZi?p=preview

Load angular template on some event

I'm quite new to angular and frontend in general, but what i'd like to see is something similar to what routing with ngView gives, but without routing, i.e just load a template on some event. To be more specific, let's say i have an input field somewhere in the header and when i click/focus on this field a special panel with different input options shows up. The trick is that this input field and other elements are already a part of a template which is loaded into ngView, so as i understand i can't use another ngView for options pane.
use ngIf, ngShow, ngHide, ngSwitch for stuff like that
<button ng-click="showStuff = true">Show Stuff</button>
<button ng-click="showStuff = false">Hide Stuff</button>
<div ng-show="showStuff">Showing Stuff</div>
<div ng-hide="showStuff">Hiding Stuff</div>
Have a look at this plunker for a quick and dirty, working example.
Note that the showStuff variable is just magically created by angular on the root scope, since I'm not using a controller.
You can load templates with ng-if and ng-include like this example:
<body ng-app="app">
<div class='container'>
<button ng-click='tmpl = true' class='btn btn-info'>Load template!</button>
<div ng-if='tmpl'>
<div ng-include="'template.html'"></div>
</div>
</div>
</body>
The ngIf directive will add element to the DOM when the argument expression is true. Then, the angular will compile the inner directive ngInclude, loading the template.

AngularJS: Make ng-model available outside of ng-repeat

In my AngularJS app, I am looping through an array object and send the value to a radio input as value. The overall idea is that the user selects a radio box and the value with be back part of $routeParams. Unfortunately, the variable {{ modelSelected }} does not seemed to be available outside of the ng-repeat. Why? The first never shows the variable {{ modelSelected }}.
Does AngularJS creates a ScopeChild within the ng-repeat?
Link to my jsfiddle example
<html ng-app>
<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]">
<h3> Selected {{ modelSelected }} shown outside paragraph</h3>
<div ng-repeat="model in models">
<p>{{ model.name }} -
<input ng-model="modelSelected" type="radio" name="patient" value="{{ model.name }}" required>
</p>
<h3> Selected {{ modelSelected }} shown inside paragraph</h3>
</div>
</body>
</html>
When dealing with primitives or using bindings generally try to prefer using a . in the model property that is being bound. So that when a child scope is created prototypical inheritance will carry down the properties (which are reference types) down to the children. (using $parent almost always is considered as a hack). In your case the property modelSelected is a primitive (even if it is present on the parent scope) and ng-repeat creates a child scope which does not finds this property (since inheritance does not carry it down even if it were present) and creates a new property on its scope, so the changes made to that are not seen to the outer scope.
Initialize a property on the scope as an object, selection={} in your scope (in your example it would be the outerscope). And bind the model as ng-model="selection.modelSelected
Ex:-
<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]; selection={}">
<h3> Selected {{ selection.modelSelected }} shown outside paragraph</h3>
<div ng-repeat="model in models">
<p>{{ model.name }} -
<input ng-model="selection.modelSelected" type="radio" name="patient" value="{{ model.name }}" required>
</p>
<h3> Selected {{ selection.modelSelected }} shown inside paragraph</h3>
</div>
</body>
Demo
Read: Understanding Scopes
Yes. It does create a new scope that inherits the parent scope. Use $parent.modelSelected to access the parent scope's variable.
<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]">
<h3> Selected {{ modelSelected }} shown outside paragraph</h3>
<div ng-repeat="model in models">
<p>{{ model.name }} -
<input ng-model="$parent.modelSelected" type="radio" name="patient" value="{{ model.name }}" required>
</p>
<h3> Selected {{ modelSelected }} shown inside paragraph</h3>
</div>
</body>
The {{modelSelected}} would not be available outside the ng-repeat because of how scope works in AngularJS (see https://docs.angularjs.org/guide/scope)
Scopes are arranged in hierarchical structure which mimic the DOM structure of the application
So since the <h3> element you are trying to access the {{modelSelected}} object from is outside the scope of the iterator, the object is not available.

Bind tag name in AngularJs

I want to bind tag name to variable in AngularJs. Direct way doesn't work:
<div ng-app ng-init="list=['pre', 'div', 'em']">
Check the list: {{list}}
<div data-ng-repeat="item in list">
{{item}}: <{{item}}>content</{{item}}>
</div>
</div>
How to do it right?
​
You're going to want to make a Directive and use the $compile service module.
Angular template system works on DOM tree, not on strings, so template must be valid HTML and usage of {{}} for tagname is impossible. We can write own directive for it (see Max answer) or if here is small set of options it can be more easy to use ng-include and set of templates for options.

Categories