I'm using this angular treeview project:
https://github.com/nickperkinslondon/angular-bootstrap-nav-tree
I think that this treeview haven't got functions to do searches over treeview, so I implemented mine using a form to write the label to find.
<form name="searchForm" novalidate style="margin-bottom: 50px">
<div> <input ng-model="search" type="text" class="form-control" placeholder="Buscar..." name="searchTerm" required />
</div>
</form>
This form has a .watch to detect when the user writes some text:
$scope.$watch('search', function(newTerm, oldTerm) {
filteredResponse = !newTerm ? response : updateFilteredResponse(normalize(newTerm));
updateTree();
}, true);
The function 'updateFilteredResponse' filter the nodes with label containing newTerm over original data set (read from json) and returns an array with the items to show in treeview.
The 'updateTree' function use this array and transform my custom items in the array to treeview items to add to the treeview. This items are added to
$scope.tree_data = [];
And this array is the one that uses abn-tree directive:
<abn-tree tree-data="tree_data" tree-control="my_tree" ng-if="loaded" expand-level = "2"></abn-tree>
This part is working fine. My problem comes when the result treeview is shown at screen, the treeview always appears completely collapsed.
If I put a button similar to the library example code like this:
<div style="vertical-align:top">
<button ng-click="my_tree.expand_all()" class="btn btn-default btn-sm">Expand All</button>
</div>
And declaring this in the controller as the example:
var tree;
$scope.my_tree = tree = {};
When the users click the button to expand all over the search results, it works fine. By I need to auto-expand the treeview after a search, and remove the expand-all-button.
For that, I'm trying to call my_tree.expand_all() in my controller. I tried different calls:
$scope.my_tree.expand_all();
tree.expand_all();
In different parts of my controller and my html (using ngIf and onload directives). Even I tried to do a 'watch' over $scope.my_tree for try to use the expand_all() function when var is prepared but I always have the same error:
$scope.my_tree.expand_all is not a function
Can anyone help me with that please?
You can put expand_all() function into setTimeout function as below.
setTimeout(function() {
$scope.my_tree.expand_all();
$scope.$digest();
}, 0);
It have to do because it take some delay time while binding data to treeview.
Related
I am stuck with minor issue.
I want to render list of data from api call, on input type change event.
What my app does is, when user start typing in input type, onchange event is triggered, based on that, I want to return data from api call. So that I can give autosuggestion and let user select that data.
Basically its same like how google place autosuggest work. I just want to customize that in my own list view.
<ion-content ng-controller="Googleplacesuggestion">
<h1>Search</h1>
<label class = "item item-input">
<input type ="text" ng-model="search" ng-change="getGooglePlaceSuggestionAutocomplete(search)"
class="search-query" id="address_search" placeholder="Search">
</label>
<ul class="unstyled">
<li ng-repeat="data in returnedData">
<span> {{ data }} </span>
</li>
</ul>
this is my controller
.controller('Googleplacesuggestion',function($scope,$http){
$scope.getGooglePlaceSuggestionAutocomplete = function($scope){
console.log($scope);
$scope.returnedData = [
{text:'learn angular', done:true},
{text:'build an angular app', done:false}
];
Thanks
Better to post your code but here I would suggest based on my understanding. onchange event callback is from Javascript and is an Asynchronous from which Angular is unaware of so in your callback, wrap the code inside the function in the $timeout(function() {}) method call which will tell Angular that something has changed.
For example:
<input type="text" onchange="valueChanged()" />
In your controller:
$scope.valueChanged = function() {
$timeout(function() {
/// your code
});
};
Another best option which is recommended to use ng-change directive of Angular, in this way you don't have to wrap your code inside the $timeout service.
Also, there are various libraries out there which provide your autocomplete code. https://angular-ui.github.io/bootstrap/#/typeahead is one of them if you are using BootStrap.
search something about debouncing this is technique to wait some time before doing some job, or super easy way to check string length
I am using a Kendo template with an array to dynamically add rows to a form, however, when I push an item onto the array, it adds two rows, both bound to the same data object in the MVVM (so with two objects, there are four rows). I've run the page with a debugger; line in the template, and as suspected, it hits twice before it finishes.
And what's weirder is that it renders the rows in order, then in reverse order, so if I make a change on the first row, it then makes the same change to the last row (since they are bound to the same object in the array), etc.
HTML
Here is the HTML where the form resides (the observable object classInfo is already bound to the <form> tag, hence the reason it's missing from the data-bind):
<fieldset id="classWhen">
<p>
<!-- other form stuff -->
</p>
<div id="classDates" data-bind="source: classInfo.ClassDates" data-template="classDateTemplate"></div>
</fieldset>
Kendo Template
Here is the template which is a row that contains a dropdown list and two date pickers:
<script id='classDateTemplate' type='text/kendo-ui-template'>
<p>
<select class="classDateTypeDropdown">
<option><!-- TYPE 1 --></option>
<option><!-- TYPE 2 --></option>
<option><!-- TYPE 3 --></option>
<option><!-- TYPE 4 --></option>
</select>
<input class="classDatePicker" data-bind="value: DateStart" style="width: 125px;" /> to
<input class="classDatePicker" data-bind="value: DateStop" style="width: 125px;" />
</p>
</script>
Kendo Observable Object
Here is the Kendo Observable which has an array, which is formatted as such:
var classModel = new kendo.observable({
classInfo: {
//.....
ClassDates: [],
//.....
}
});
Javascript push Function
And an addDate() function which pushes a new item to the array:
function addDate() {
classModel.get("classInfo.ClassDates").push({
"ClassType": "Type 1",
"DateStart": null,
"DateStop": null
});
//change inputs to DatePickers
//change select to DropDownList
}
I have tried running it without creating the DropDownList and DatePickers, using the basic HTML elements, but with the same result. Any help would be greatly appreciated.
So, I'm not sure why this was happening (some research will need to be involved), but the cause of the problem was with my binding. Apparently the Kendo template does not like binding to object arrays that belong to other objects, as I have with classInfo.ClassDates.
I changed the bindings from:
kendo.bind($('#addClassWindow'), classModel);
<div data-bind="source:classInfo.ClassDates"data-template="classDateTemplate"></div>
to:
kendo.bind($('#addClassWindow'), classModel.classInfo);
<div data-bind="source:ClassDates"data-template="classDateTemplate"></div>
and now it works fine. For whatever reason.
I have the following code:
<input id="id">
<button data-action="bea" ng-click="Create($('#id1')[0].value);" class="btn">Insert ID</button>
<button data-action="bea" ng-click="Create($('#id2')[0].value);" class="btn">Insert ID</button>
In the JS I have:
$scope.Create = function (id){
if (id === undefined) {
$scope.data = "You must specify an id";
} else {
$scope.data = data;
console.log(data);
});
}
};
When the call gets into the Create function the value of the id is undefined.
If I add the following line at the beginging of the Create function everything works ok:
id = $('#id')[0].value;
If I send a constant value it works:
<button data-action="bea" ng-click="Create('SomeID');" class="btn">Insert ID</button>
Why is this happening and how can I do that without putting the line of value into the method?
Thanks
This is just an extension of comments and other answers, You could achieve this in many ways using angular, one simple example could be:-
<!-- Add a controller -->
<div ng-controller="MainCtrl">
<!-- Give a model binding to your text input -->
<input ng-model="userEntry" type="text"/>
<!-- ng-click pass which ever argument you need to pass, provided it is an expression that can be evaluated against the scope or any constants -->
<button data-action="bea" ng-click="Create(userEntry);" class="btn">Insert ID</button>
<!-- Some simple data binding using interpolation -->
{{data}}
<!-- Just for demo on repeater on a list of items on the scope -->
<div ng-repeat="item in items track by $index">{{item}}</div>
</div>
Example Demo
My 2 cents on the lines of what were originally trying to do:-
Use angular bindings instead of accessing DOM directly for getting the data, it really helps you deal with just the data without worrying about how to access or render it in DOM. If you think you need to access DOM for implementing business logic re-think on the design, if you really need to do it, do it in a directive. Angular is very opinionated on the design and when where you do DOM access.
ng-model
ng-binding
controller
all about ngmodel controller
This is not the way you should do in AngularJS. You should really think in Angular if you want to use AngularJS. Refer this post ("Thinking in AngularJS" if I have a jQuery background?)
All DOM manipulation should be done in Directive. Refer this page that I found really clear.
(http://ng-learn.org/2014/01/Dom-Manipulations/)
My guess is that $ is not bound to the jQuery function when the ng-click value is evaluated, because it is not exposed in the Angular scope.
Solutions to adress this:
expose the jQuery function in scope somewhere, e.g $scope.$ = $; in a controller.
make the Create function parameterless as you suggested, with a var id = $('#id')[0].value; at the beginning
my favorite : avoid using jQuery. If you put some data in the #id element, there's probably a more natural and AngularJS-idiomatic way of retrieving it than querying the DOM (e.g an Angular service).
In particular, if the element you're targeting is an <input> element, then use the ngModel directive to link the value to a $scopeproperty that will be accessible in the controller :
<input ng-model="inputData"/>
The JavaScript you are trying to pass as a parameter of the create function is not available in the scope of the Create function.
Try to target the element a different way.
Does that help?
I'm starting with AngularJS, and I'm building a multi-step form where user has to fill different pages. When finished a page, he's allowed to press a next button and fill the following page.
For the first page, I've built in the HMTL a form (named pageOneForm), with different text input fields, marked as required, and in the relative controller I'm doing this watch:
$scope.$watch('pageOneForm.$valid', function(validity) {
ModelData.actualPageCompleted = validity;
})
And it works like a charme. My model (ModelData) is updated.
I was trying to apply the same logic to the following part of the app, the second page. Instead of input text, the user has to select two options from 2 different radio buttons groups.
So I built in the html a list of buttons via ng-repeat :
<div ng-Controller="PageTwo" ng-show='data.actualPage == 2'>
<form name="pageTwoForm">
<h3>General Information > Knowledge About </h3>
<div>
<b>User</b>
<div ng-repeat="option in userOptions">
<input type="radio" name="userGroups" ng-model="data.knowledgeAboutUser" ng-value="option.id" id="{{option.id}}" required>{{option.text}}
</div>
<div ng-repeat="option in targetGroupUserOptions">
<input type="radio" name = "targetUserGroup" ng-model="data.knowledgeAboutTargetGroup" ng-value="option.id" id="{{option.id}}" required>{{option.text}}
</div>
</div>
</form>
and I've implemented the same code as above in its controller:
$scope.$watch('pageTwoForm.$valid', function(validity) {
ModelData.actualPageCompleted = validity;
})
but apparently it doesn't work, and in my model actualPageCompleted is always true...
What am I doing wrong?
Thanks
I did my best to create a controller with some dummy data to get a fiddle working with your example code. Here is the fiddle You need to force the $digest cycle to update your form's validity state on ng-click for the radio buttons (see this SO post for more details), which is why the method
$scope.forceDigest = function(){
setTimeout(function(){ $rootScope.$$phase || $rootScope.$apply(); });
};
is necessary. Alternatively, you can get rid of the method call and uncomment the html code
<h3 ng-show="false">{{data.knowledgeAboutTargetGroup}}</h3>
<h3 ng-show="false">{{data.knowledgeAboutUser}}</h3>
in the fiddle to force the form object to update as well.
And I would make sure that ModelData.actualPageCompleted is not retaining its true value from when pageOneForm.$valid became true and it was set.
I hope that this helps!
I'm using a form to add elements to list that is displayed on the side of the form.
Markup is:
<form name="thingForm">
<input required type="text" ng-model="thing.name"/>
<input required type="text" ng-model="thing.value"/>
<input type="submit" ng-click="addThing(thing)"/>
</form>
<ul>
<li ng-repeat="thing in things">{{thing.name}} with value of {{thing.value}}</li>
</ul>
And in a controller I have:
$scope.things = [];
$scope.addThing = function(thing) {
$scope.things.push(thing);
$scope.thing = {};
};
Working jsfiddle: http://jsfiddle.net/cXU2H/1/
Now as you can see, I can empty the form by emptying the model, however since the inputs have the required tag the browser still displays an error message (at least Chrome does).
I looked at the similar questions and:
I've also looked at this answer: https://stackoverflow.com/a/16296941/545925 however the jsfiddle behaves exactly the same as in my example: after the input is cleared it still has an ng-invalid-required class remaining (and it also triggers a HTML5 error message)
since I'm not on the 1.1.x branch $setPristine() is not available for me $setPristine() behaves the same way
I can of course write a function that iterates through the elements of a form and removes every ng-invalid-required and ng-invalid class, but that is not the way I would like to solve this. That is what I would do with jQuery.
Are you using $setPristine right? You can easily see in your fiddle that if you add it, it works. http://jsfiddle.net/X6brs/
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.things = [];
$scope.addThing = function(thing) {
$scope.things.push(thing);
$scope.thing = {};
$scope.thingForm.$setPristine(true);
};
}
$scope.thingForm.$setPristine();
$scope.thingForm.$setUntouched();
will do the trick.