I am new to Angular and may be missing something obvious. I did read the ng-model scope documentation. And followed the recommendation to use an object instead of primitives.
I also tried the $parent suggested by a few others here. That didn't help either.
I have read many Q&As here involving ng-repeat. But they are talking about an input inside an ng-repeat. In my case there is only one text box. I am trying to search inside a tree using ng-repeat.
<script type="text/ng-template" id="tree_item_renderer.html">
{{data.name}}
<ul>
<li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'" ng-show="visible(data)"></li>
</ul>
</script>
This is the function being called:
$scope.visible = function(item) {
console.log("SearchText inside visible: " + $scope.input.searchText);
return !($scope.input.searchText && $scope.input.searchText.length > 0
&& item.title.indexOf($scope.input.searchText) == -1);
};
Take a look at my jsFiddle
I saw your code on the jsFiddle. There is an issue that you called ng-controller="TreeController" twice and initialized ng-app = Application. I have commented out these two lines of code, and you can see the change on the console.
The reason why you are getting this problem is you make Angular to re-initialize TreeController which it clears the value of input.searchText.
Check out the working jsFiddle !
<div ng-app="myApp">
<div ng-controller="TreeController">
Search UIC:
<input type="text" ng-change="search()" ng-model="input.searchText"
placeholder="Enter your search terms" />
<!-- <ul ng-app="Application" ng-controller="TreeController"> -->
<ul>
<li ng-repeat="data in tree" ng-include="'tree_item_renderer.html'" ng-show="visible(data)">
</li>
</ul>
</div>
</div>
Related
I have been teaching myself angular and am currently following this tutorial, https://docs.angularjs.org/tutorial, except I haven't cloned the repo down and am using different data to keep it interesting.
My issue is probably a very simple mistake I am making but I'm still trying to understand the concepts/jargon of the framework which makes it hard to search for an answer.
When my template looks like this ..
<div>
<ul>
<li ng-repeat="electorate in $ctrl.electorates | filter:$ctrl.query | orderBy:$ctrl.orderProp">
<p>{{electorate.electorate}}</p>
<p>{{electorate.mp}} </p>
<p>{{electorate.party}} </p>
</li>
</ul>
<div> Search: <input ng-model="$ctrl.query"/>
<p>
Sort by:
<select ng-model="$ctrl.orderProp">
<option value="party">Party</option>
<option value="mp">MP - Alphabetical</option>
</p>
</div>
</div>
.. I can type in the search input box to filter the ng-repeat list and order them by the sort by options just fine.
But it would look better with the search/sort by options above the data like so..
<div>
<div> Search: <input ng-model="$ctrl.query"/>
<p>
Sort by:
<select ng-model="$ctrl.orderProp">
<option value="party">Party</option>
<option value="mp">MP - Alphabetical</option>
</p>
</div>
<ul>
<li ng-repeat="electorate in $ctrl.electorates | filter:$ctrl.query | orderBy:$ctrl.orderProp">
<p>{{electorate.electorate}}</p>
<p>{{electorate.mp}} </p>
<p>{{electorate.party}} </p>
</li>
</ul>
</div>
.. except while it shows the search/sort by options, the ng-repeat data never appears.
Why is this and how do I make it work in the way I want?
I don't see any big differences between what I have done and the tutorial (specifically around steps 3 - 7) and my guess was it is due to the search/sort referencing the data before it is ready, however in the tutorial it is structured this way no problem.
Here is my controller as well
angular.
module('electorateList').
component('electorateList', {
templateUrl: 'electorate-list/electorate-list.template.html',
controller: function ElectorateListController($http){
var self = this;
self.orderProp = 'party';
$http.get('electorates.json').then(function(response){
self.electorates = response.data;
});
}
});
Thanks and feel free to point me to a duplicate etc if there is one.
Edit: Thanks to Lex for pointing out I was missing a closing tag for the select element, resolving that fixed the issue.
You are missing a closing </select> tag.
Using Laravel for my API and the error response is like this:
I set that as a scope called errors and do a ng repeat to show them:
<ul ng-show="errors" class="list errors-list">
<li class="item" ng-repeat="error in errors">{{error}}</li>
</div>
However, this results in:
["The title field is required."]
Is there a 'best' way to just get the text and not the speech marks or brackets?
I know I can just search and replace them but always like to know if there is a best way to do something.
Just pull the first element
<ul ng-show="errors" class="list errors-list">
<li class="item" ng-repeat="error in errors">{{error[0]}}</li>
</div>
Quoting the documentation of AngularJS expressions, these are examples of valid expressions in Angular:
1+2
a+b
user.name
items[index] <--- This is what we're using for this answer
I'm building a dashboard similar to this one Admin Dashboard Theme
I'm implementing the Right Side SideBar as show in the screenshot
My App code structure is something like:
index.html:
<div ui-view=""></div>
main.html
<div ui-view=""></div>
<div ng-include='"app/dashboard/sidebar.html"'></div>
I have done nesting of view in my main.html where I'm injecting different views. Also, since my right side sidebar is fixed, I want it to be common in all the main views. So, I have just included it in my main.html.
Now, the problem is somehow my sidebar.html is getting initialized again and again no matter if I scroll my page down or perform any action inside sidebar. I have verified it by printing console logs for every controller function which are used in sidebar.html view.
This problem is related to my this post: Earlier, I wasn't able to figure out the actual issue.
Following is my controller and jade code:
angular.module('myApp')
.controller('SidebarCtrl', function($scope, $rootScope) {
$scope.message = {};
$scope.addSideBarToggleClass = function() {
console.log("addSideBarToggleClass");
return true;
}
$scope.getStatusClass = function(status) {
console.log("getStatusClass");
return 'is-online';
}
$scope.openChat = function(receiver) {
console.log("openChat");
}
// etc...
});
<aside ng-class="{ 'control-sidebar-open' : addSideBarToggleClass()}"
ng-controller="SidebarCtrl">
<ul>
<li ng-class="{active: isTabSelected('chat')}">
<a data-toggle="tab" ng-click="updateCurrenTab('chat')"></a>
</li>
<li ng-class="{active: isTabSelected('home')}">
<a data-toggle="tab" ng-click="updateCurrenTab('home')"></a>
</li>
</ul>
<div>
<div ng-class="{active: isTabSelected('home')}">
<h3>Recent Activity</h3>
</div>
<div ng-class="{active: isTabSelected('chat')}">
<div>
<h4>Chat {{noOfUsersOnline}}</h4>
<div>Friends
<a href="#" ng-repeat="user in users" ng-click="openChat(user)">
<span ng-class="getStatusClass(user.status)"></span>
{{user.name}}</a>
</div>
</div>
</div>
</div>
</aside>
I see many logs of "addSideBarToggleClass", "getStatusClass" and every time I click on openChat, I see a log of "openChat" and then again "addSideBarToggleClass" and "getStatusClass"
Can anyone please point out what can be the possible problem for this behavior?
You need to familiarize yourself with the concept of a digest loop in Angular.
In short, every time a digest loop runs, all expressions, e.g. {{name}} or ng-show="isActive && isEnabled", that are being "$watched" by Angular are evaluated (sometimes more than once). This means that if you are invoking a function inside an expression:
<div ng-show="isShown()">
$scope.isShown = function(){
console.log("expect to see this text a lot of times");
return true;
};
the function will be executed on every digest loop.
A digest loop runs any time that something in Angular calls $scope.$digest, which by default happens on things like ng-click or ng-change or $http.then, etc..
I'm building a project with AngularJS using AngularUI directives: ui.tinymce and ui.sortable.
Here is my code:
<ul ui-sortable="sortableOptions" ng-model="appData.data">
<li ng-repeat="box in appData.data" boxlistitem>
<textarea ng-model="box.title" value="{{box.title}}" class="tinyMCE" ui-tinymce="tinymceOptions"></textarea>
</li>
</ul>
Basically it works great, but a strange thing happens once I'm sorting one of the list items - the tinyMCE not working any more (see the attached images - the left one is how it suppose to be).
Any idea what might be the problem?
Thanks,
Daniel
Daniel,
It's a known issue, I have managed to work it out with ng-include, so that the tinymce textarea will be in it.
like this:
<ul ui-sortable="sortableOptions" ng-model="appData.data">
<li ng-repeat="box in appData.data" boxlistitem ng-include src="box.template_name">
</li>
</ul>
<script type="text/ng-template" id="tinymce_module">
<textarea ng-model="box.title" value="{{box.title}}" class="tinyMCE" ui-tinymce="tinymceOptions"></textarea>
</script>
and in the controller you need to do something like this:
$scope.sortableOptions =
update: (e, ui) ->
box.template_name = ""
$timeout ->
box.template_name = "tinymce_module"
,0
just find your way to pass the {{box}} to the update function in sortableOptions, that will reload the tinymce plugin with all of it's data.
Good luck.
Is there a way to apply a single view model to several elements?
Basically I have a section of html that the view model (VM1) should be bound to, and I have another view model (VM2) that needs to be bound to a subsection of this. KO doesn't seem to like this though (I even tried using ko.cleanNode(element) on the subsection). So what I'm trying to do is be more specific in my binding by applying it to each piece that needs it. This is hard to explain without code, so here we go:
<section>
<ul>
<li id="one">...</li>
<li id="two">...</li>
<li id="three">...</li>
<li id="diffmodel">...</li>
</ul>
</section>
What I currently have is VM1 being bound to <section>, and VM2 being bound to #diffmodel, but KO doesn't seem to like this.
My current objective (and the question proposed) is to apply VM1 to #one, #two, and #three, and VM2 to #diffmodel, but that doesn't seem to work either (VM1 isn't being bound at all).
Is there a nice solution to this type of situation?
A pretty easy solution is to use a custom binding to prevent the children of an element from being bound.
It would be something like:
ko.bindingHandlers.ignoreBindings = {
init: function() {
return { controlsDescendantBindings: true };
}
};
var VM1 = {
valueOne: ko.observable("one"),
valueTwo: ko.observable("two"),
valueThree: ko.observable("three")
};
var VM2 = {
different: ko.observable("different")
};
ko.applyBindings(VM1);
ko.applyBindings(VM2, document.getElementById("diffmodel"));
HTML:
<section>
<ul>
<li id="one" data-bind="text: valueOne"></li>
<li id="two" data-bind="text: valueTwo"></li>
<li id="three" data-bind="text: valueThree"></li>
<li data-bind="ignoreBindings: true">
<div id="diffmodel" data-bind="text: different"></div>
</li>
</ul>
</section>
Sample here: http://jsfiddle.net/rniemeyer/FesgK/
In KO 2.1 (in RC at the moment), the ignoreBindings custom binding could even be used as a containerless binding like: http://jsfiddle.net/rniemeyer/FesgK/1/