Angular-Js unable to push data into array of controller - angularjs

HTML:-
<div ng-controller="countryController">
{{name}}
<ul>
<li ng-repeat="country in countries">
{{ country.name }}
<ul ng-show="country.states.length > 0">
<li ng-repeat="state in country.states">
{{ state.name }}
</li>
</ul>
<input type="text" ng-model="newState">
<a href ng-click="addStateState(country)">Add </a>
{{newState}}
</li>
</ul>
</div>
When I key in newState model it appears in client side.
Now i try to get that value into controller and try to push in array of states, its unable to add into states array.
JS Controller:-
myApp.factory('countryService', function($http){
var baseUrl = 'services/'
return {
getCountries : function(){
return $http.get(baseUrl + 'getcountries.php');
}
};
});
myApp.controller('countryController', function($scope, countryService){
countryService.getCountries().success(function(data){
$scope.countries = data;
});
$scope.addStateState = function(country){
country.states.push({'name' : $scope.newState});
$scope.newState = "";
};
});

Your main issue is that your $scope.newState is not available inside $scope.newStateState function. It is bad practice to manipulate an object like $scope within a $scope function. In fact, you are creating multiple multiple objects that are not the same as the $scope that you inject; otherwise, the input boxes for two different countries should match.
Working Plunkr below.
http://plnkr.co/edit/HG3zWG?p=preview
JS:
angular.module('myApp',[])
.controller('countryController', function($scope){
$scope.countries = [
{ name: 'USA', states:[
{name: 'Virginia'},
{name: 'Utah'}
]
},
{ name: 'Brazil', states:[
{name: 'Pernabuco'}
]
}
];
$scope.addStateState = function(country, data){
country.states.push({'name' : data.newState});
data.newState = "";
};
});
HTML:
<div ng-controller="countryController">
{{name}}
<ul>
<li ng-repeat="country in countries">
{{ country.name }}
<ul ng-show="country.states.length > 0">
<li ng-repeat="state in country.states">
{{ state.name }}
</li>
</ul>
<input type="text" ng-model="data.newState" />
Add
{{data.newState}}
</li>
</ul>
</div>

When you are dealing with things like ng-repeat, ng-if, or ng-include, they each create a new scope. This means that when you try to bind to a primitive on the root level of that scope, it will get dereferences when you update the value. So, you need to put your primitives inside of an object. John Papa's style guide recommends making a "vm" property on your scope. vm stands for view model.
Here is a jsfiddle of how to use it.
http://jsfiddle.net/fbLycnpg/
vm.newState

Related

Watch attribute-values of multiple elements without directive?

Given the following:
<div ng-app="interactive">
<main ng-controller="RecipesController">
<ul>
<li ng-repeat="option in options">
<ul decisionIndex="{{ value }}">
<li>Edit {{ option.name }}</li>
<li>Choose {{ option.name }}</li>
<li>Delete {{ option.name }}</li>
</ul>
</li>
</ul>
</main>
</div>
How can I use $watch to detect whether "value" (is either 0, 1 or 2) changes?
Do I need to use $watchGroup ? As far as I know that only works if you have multiple attributes which are not the same.
I found this working with directives and isolated scopes, but it does not quite match my case and I would prefer not using directives. Is it possible using isolated scopes without directives? Or how can I watch for changes of an attribute-value occuring multiple times?
EDIT:
This is how I tried using $watch
app.controller('RecipesController', ['$scope', function ($scope) {
$scope.$watch('value', function () {
console.log('value: ' + $scope.value);
});
}]);
<div ng-app="myApp" ng-controller="myCtrl">
<input type="text" ng-model="value">
<p>Current value is: {{value}}</p>
<ul>
<li ng-repeat="option in options">
<ul decisionIndex="{{value}}">
<li ng-click="setValue(0)">Edit {{option.name}}</li>
<li ng-click="setValue(1)">Choose {{option.name}}</li>
<li ng-click="setValue(2)">Delete {{option.name}}</li>
</ul>
</li>
</ul>
</div>
Controller:
angular.module('myApp', [])
.controller('myCtrl', ['$scope', function($scope) {
$scope.value = 0; //Set initial value, or just declare it;
$scope.options = [
{name: "Bob"},
];
$scope.setValue = function(a){
$scope.value = a;
}
$scope.$watch('value', function(newVal, oldVal){
console.log(newVal);
})
}]);

Detect user selection for li

I have a list of items ("locals" array) which I show in a list
<ul class="list-group">
<li ng-repeat="loc in locals" class="list-group-item"><a href="" data-id={{loc.ID}}>{{loc.location}}</a>
</li>
</ul>
I want the user to be able to select an item, and then to use this item in code.
What is the preferred way to do it.
Also I am creating the application for mobile, so I should be able to know that the user chose this item in mobile( and not just use mouseclick for example).
You can make use of angular js ng-click event (on the li item where ng-repeat is and do something like this: fiddle
code snippet of controller:
function MyCtrl($scope) {
$scope.templateList = [{id:1, name: 'Template1'}, {id:2, name: 'Another Template'}]
$scope.template = {};
$scope.setValue = function(list) {
$scope.template.template_id = list.id;
$scope.template.template_name = list.name;
}
}
Of HTML:
<div ng-app>
<form ng-controller="MyCtrl">
<input type="hidden" name="template_id" ng-model="template.template_id" />
<input type="text" name="template_name" ng-model="template.template_name" />
<ul>
<li ng-repeat="list in templateList" ng-click="setValue(list)">{{list.name}}</li>
</ul>
</form>
</div>
Try this:
In html,
<ul class="list-group">
<li ng-repeat="loc in locals" class="list-group-item">
<a href="" data-id={{loc.ID}} ng-click="selectLoc(loc.location)">
{{loc.location}}
</a>
</li>
</ul>
In JS,
$scope.selectLoc = function(location){
console.log(location);
//Here you will get the selected location.
var SomeVar = location;
}
Hope this helps....

Removing Class from all children except clicked child on ng-click in Angular

I have a simple list item being parsed with ng-repeat:
<ul>
<li ng-repeat="item in items" class="commonClass" ng-class="{'on': on_var}" ng-click="on_var=!on_var">
{{item.name}}
<li>
</ul>
clicking on a list-item will add the class name 'on' as expected. but I want to remove all other 'on' classes as soon as you click on another list-item and only add it to the one clicked. I come from a jQuery background and I am new to angular. All I want to do is something like this:
$("li.commanClass").click(function(){
$("li.commonClass").removeClass('on');
$(this).addClass('on');
})
I want to know what is the "angular way" of achieving this result
jsfiddle
Thanks.
In angular you want to pass object references to child scopes as much as possible. This way you can use prototypical object inheritance that can branch down many levels.
Think of a scope as being a model. In the case of ng-repeat it creates a child scope for each element. So if the parent scope holds a model it will be passed as reference to the child scopes. Each of these child scopes will hold a reference to the parent scope model object.
Thus you can do:
<li ng-repeat="item in model.items"
ng-class="{'on': model.selected==item}"
ng-click="model.selected=item">{{ item.name }}</li>
Then in controller:
$scope.model = {
selected: null,
items = [
{name: "Apple"},
{name: "Banana"},
{name: "California"}
]
};
Try to avoid using functions as above answer does. These functions will get called many times and will add unnecessary extra overhead.
Valuable post to read: "Thinking in AngularJS" if I have a jQuery background?
DEMO
You can add a variable to your scope to maintain which item is selected, and a function on your scope that toggles the variable.
Controller:
app.controller('myCtrl', function($scope) {
$scope.items =
[
{name: "Apple"},
{name: "Banana"},
{name: "California"}
]
$scope.selectItem = function( item ) {
$scope.selectedItem = item;
};
})
HTML:
<div ng-app="myApp">
<ul ng-controller="myCtrl">
<li ng-repeat="item in items" class="commonClass" ng-class="{'on': selectedItem === item}" ng-click="selectItem(item)">
{{ item.name }}
</li>
</ul>
</div>
Fiddle coming at ya of jsparks answer:
http://jsfiddle.net/eHDTF/
See fiddle for code!
I think you should refer a directive for whole your project instead of controller.
<div class="nav-item">
<ul>
<li active-me="on">Item 1</li>
<li active-me="on">Item 2</li>
<li active-me="on">Item 3</li>
<li active-me="on">Item 4</li>
<li active-me="on">Item 5</li>
</ul>
</div>
Then create activeMe directive:
angular.module('app').directive('activeMe', function(){
return {
restrict: 'A',
scope: {
activeMe: '#'
},
link: function(scope, element, attrs) {
element.bind('click', function() {
if (scope.activeMe !== undefined && scope.activeMe.trim().length > 0) {
element.parent().children().removeClass(scope.activeMe);
element.addClass(scope.activeMe);
} else {
element.parent().children().removeClass('active');
element.addClass('active');
}
});
}
};
});

Scope issue with ui-bootstrap tabs

I'm having a problem understanding scope. ui-bootstrap creates a new scope when you use tabset, I get that. I thought parent methods were available to child scopes? Here is a jsbin showing my problem.
JSBin
I'm sure there is something simple I'm missing, but I can't see it.
Code inline in case JSBin acts up:
angular.module('app', ['ui.bootstrap'])
.controller('MainController', function($scope) {
$scope.filters = [];
$scope.search = '';
$scope.providerVersions = [
{ name:'SomeOS', type:'pv' },
{ name:'OtherOS', type:'pv' }
];
$scope.scripts = [
{ name:'Something', pv:'SomeOS', type:'script' },
{ name:'Somebody', pv:'SomeOS', type:'script' },
{ name:'Other thing', pv:'OtherOS', type:'script' },
{ name:'Other body', pv:'OtherOS', type:'script' }
];
$scope.addFilter = function(f) {
$scope.filters.push({ name:f.name, type:f.type });
};
$scope.remFilter = function(i) {
$scope.filters.splice(i,1);
};
$scope.filterByName = function(n) {
var name = n.name.toLowerCase();
var search = $scope.search.toLowerCase();
return name.indexOf(search) > -1;
};
$scope.filterByFilters = function(f) {
if ($scope.filters.length===0) { return true; }
var byName = _.where($scope.filters, { type:'script' });
if (byName.length > 0) {
return _.contains(_.pluck(byName, 'name'), f.name);
}
return _.contains(_.pluck($scope.filters, 'name'), f.pv);
};
});
HTML
<body ng-app="app">
<div ng-controller="MainController">
<h3>This works</h3>
<p>Filters on both name and role as expected.</p>
<div ng-repeat="s in scripts|filter:filterByFilters">
{{ s.name }}
</div>
<form name="searchForm">
<input type="text" class="form-control" placeholder="Filter by name or role" ng-model="search">
</form>
<span ng-repeat="f in filters"><a ng-click="remFilter($index)">{{ f.name }}</a><span ng-if="!$last">, </span></span>
<ul ng-show="search.length>0" class="dropdown-menu" style="display:block; position:static;">
<li class="dropdown-header">Name</li>
<li ng-repeat="s in scripts|filter:filterByName"><a ng-click="addFilter(s)">{{ s.name }}</a></li>
<li class="divider"></li>
<li class="dropdown-header">Role</li>
<li ng-repeat="p in providerVersions|filter:search"><a ng-click="addFilter(p)">{{ p.name }}</a></li>
</ul>
<h3>This does not work</h3>
<p>Only filters on role. It does not call $scope.filterByName.</p>
<tabset>
<tab heading="Scripts">
<div ng-repeat="s in scripts|filter:filterByFilters">
{{ s.name }}
</div>
<form name="searchForm">
<input type="text" class="form-control" placeholder="Filter by name or role" ng-model="search">
</form>
<span ng-repeat="f in filters"><a ng-click="remFilter($index)">{{ f.name }}</a><span ng-if="!$last">, </span></span>
<ul ng-show="search.length>0" class="dropdown-menu" style="display:block; position:static;">
<li class="dropdown-header">Name</li>
<li ng-repeat="s in scripts|filter:filterByName"><a ng-click="addFilter(s)">{{ s.name }}</a></li>
<li class="divider"></li>
<li class="dropdown-header">Role</li>
<li ng-repeat="p in providerVersions|filter:search"><a ng-click="addFilter(p)">{{ p.name }}</a></li>
</ul>
</tab>
</tabset>
</div>
</body>
Scopes are interesting and confusing. All scopes, except $rootScape, have ancestors. I don't know for certain but I assume that the ui-bootstrap tabset creates its own isolate scope. That isolate scope does have a parent, but you've "isolated" it so it can't see any attributes of an ancestor scopes unless you've specifically included them within the directive. Should an isolate scope have a child scope that is not an isolate scope, it can see and manipulate its parents attributes but nothing farther back the ancestor chain. Events can pass freely up and down the chain and you should be very careful using them since, when dealing with isolate scopes, may cause side effects you aren't expecting.
If the above was just a bunch of blather, go to https://docs.angularjs.org/guide/directive and reread the info there - maybe that will make it more clear.
Here's probably one of the best dissertation on scope you'll fnd in a quick, concise and clear explanation - What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
I was able to work around this. Honestly I don't know why this works, but it does. Maybe somebody else can fill in the gaps for me. What I did was instead of trying to filter my list, I changed the filter function to return an array, and I use that result for ng-repeat to iterate.
Old filter function
$scope.filterByName = function(n) {
var name = n.name.toLowerCase();
var search = $scope.search.toLowerCase();
return name.indexOf(search) > -1;
};
New filter function
$scope.filterByName = function(list, srch) {
var ret = [];
_.each(list, function(l) {
if (l.name.toLowerCase().indexOf(srch.toLowerCase()) > -1) {
ret.push(l);
}
});
return ret;
};
Old ng-repeat
<li ng-repeat="s in scripts|filter:filterByName">
New ng-repeat
<li ng-repeat="s in filterByName(scripts, search)">

Is it possible to initialize a data binding in Angular from HTML rather than JavaScript?

What I mean is, say I have this HTML:
<ul ng-controller="ContactsCtrl">
<li ng-repeat="contact in contacts">
<div class="name">{{name}}</div>
<div class="email">{{email}}</div>
</li>
</ul>
This works just fine if I initialize the contacts collection from JavaScript:
app.controller('ContactsCtrl', ['$scope', function($scope) {
$scope.contacts = [
{ name: 'Dan', email: 'dan#example.com' },
{ name: 'Bob', email: 'bob#example.com' }
];
}]);
What if, instead, I want to initialize the contents of the collection from the HTML side? Something like:
<ul ng-controller="ContactsCtrl" ng-model="contacts">
<li>
<div class="name" ng-bind="name">Dan</div>
<div class="email" ng-bind="email">dan#example.com</div>
</li>
<li>
<div class="name" ng-bind="name">Bob</div>
<div class="email" ng-bind="email">bob#example.com</div>
</li>
</ul>
And then from JavaScript I would have $scope.contacts initialized based on what I have in the HTML.
Is this possible?
you can do a ng-init="" in your html to initialize a variable or sets of variables but that doesn't seem to be quite what your asking. Why would you want your second example? What are you trying to do with it?
Rather use jQuery and parse HTML DOM, if You have at least... 500 records, other way is 'copy and paste' - it will be faster.
Here is short info 'how it works'
Tutorial

Resources