Why can't I do a $scope update within a jQuery scope? - javascript

I'm using typeahead to get some suggestions on an input text, this is inside a div controlled by an Angular controller.
The code for the suggestion tasks works with a jQuery plugin, so when I select, something I'm trying to assign a value to $scope, however this is NEVER happening.
I already tried getting the scope of the element with var scope = angular.element($("#providersSearchInput").scope() and then assign it as suggested here but it didn't work.
This is what I'm trying:
<div class="modal-body" ng-controller="ProvidersController" ng-init="orderReviewTab = 'observations'">
<input type="text" id="providersSearchInput" data-provide="typeahead" class="form-control input-md" placeholder="Buscar proovedores">
{{currentProvider}}
</div>
The controller looks like this:
tv3App.controller('ProvidersController', function($scope, $rootScope, $http, $timeout) {
var resultsCache = [];
$("#providersSearchInput").typeahead({
source: function (query, process) {
return $.get("/search/providers/?query=" + query, function (results) {
resultsCache = results;
return process(results);
},'json');
},
matcher: function (item) {
var name = item.name.toLowerCase();
var email = item.email.toLowerCase();
var contact_name = item.contact_name.toLowerCase();
//console.log(name);
var q = this.query.toLowerCase();
return (name.indexOf(q) != -1 || email.indexOf(q) != -1 || contact_name.indexOf(q) != -1);
},
scrollHeight: 20,
highlighter: function (itemName) {
var selected = _.find(resultsCache,{name:itemName});
var div = $('<div></div>');
var name = $('<span ></span>').html('<strong style="font-weight:bold">Empresa: </strong> ' + selected.name);
var contact = $('<span ></span>').html(' <strong style="font-weight:bold">Contacto: </strong> ' + selected.contact_name);
var email = $('<span ></span>').html(' <strong style="font-weight:bold">e-mail:</strong> ' + selected.email);
return $(div).append(name).append(contact).append(email).html();
},
minLength: 3,
items: 15,
afterSelect: function (item) {
console.log(item);
$scope.$emit('providerSelected',item);
}
});
$scope.$on('providerSelected', function (event,provider) {
console.log(provider);
$scope.currentProvider = provider;
$scope.$apply();
});
});
Edit
I tried this to check any changes:
$scope.$watch('currentProvider',function (newValue,oldValue) {
console.log(oldValue);
console.log(newValue);
});
So when selecting something it actually triggers and $scope.currentProvider seems to be updated but its never getting rendered at view ...

get https://angular-ui.github.io/bootstrap/
once you do, in your code make sure
angular.module('myModule', ['ui.bootstrap']);
and for typeahead have
<input type="text" ng-model="currentProvider" typeahead="provider for provider in getProviders($viewValue) | limitTo:8" class="form-control">
In your controller make sure you have
$scope.getProviders = function(val){
return $http.get('/search/providers/?query=' + val).then(function(response){
return response.data;
})
}
This should do the trick although I haven't tested

Related

how to bind a directive var to controller

i have a problem with using 2 way binding in angular, when i change my input, the change dosnt affect to controller. but the first init from controller affect directive.
in the picture i changed the value, but vm.date still have value test.
my directive:
(function (app) {
app.directive('datePicker', function () {
//Template
var template = function (element, attrs) {
htmltext =
'<input ng-readonly="true" type="text" id="' + attrs.elementId +
'" ng-model="' + attrs.model + '" type="date" />';
return htmltext;
}
//Manipulation
var link = function ($scope, elements, attrs, ctrls) {
//Declare variables we need
var el = '#' + attrs.elementId + '';
var m = attrs.model;
var jdate;
var date;
$scope[attrs.model] = [];
$(el).on('change', function (v) {
jdate = $(el).val();
gdate = moment(jdate, 'jYYYY/jMM/jDD').format('YYYY-MM-DD');
if (moment(gdate, 'YYYY-MM-DD', true).isValid()) {
date = new Date(gdate);
$scope[m][0] = date;
$scope[m][1] = jdate;
//console.log($scope[m]);
$scope.vm[m] = $scope[m];
console.log($scope.vm); //----> Here Console Write Right Data
} else {
//console.log('Oh, SomeThing is Wrong!');
}
});
} // end of link
return {
restrict: 'E',
scope: {vm: '='},
template: template,
link: link
};
});
}(angular.module('app')));
and my controller:
(function (app) {
app.controller('test', ['$scope', function ($scope) {
var vm = this;
vm.date = 'test';
vm.mydate = 'test2';
}]);
}(angular.module('app')));
and html:
<body ng-app="app">
<div ng-controller="test as vm">
<date-picker element-id="NN" model="vm.date" vm="vm"></date-picker>
<p>{{vm.date}}</p>
<date-picker element-id="NN2" model="vm.mydate" vm="vm"></date-picker>
<p>{{vm.mydate}}</p>
</div>
</body>
I am not sure why you made the textbox as readonly, but if you remove that readonly and try to update the textbox then the two way binding works. Here's the fiddle for that
https://fiddle.jshell.net/dzfe50om/
the answer:
Your controller has a date property, not a vm.date property. – zeroflagL May 25 at 13:48
You should define vm to $scope instead of this;
var vm = $scope;

auto-complete jquery doesn't work with with ngmodel and ngrepeat (angular)

I am trying to use autocomplete function of jquery inside my angular controller and that doesn't work may be because of ng-model or ng-repeat I don't know exactly ^^
My html file contains this code
<div ng-if="data._id" data-ng-controller="AddonExclusivityCtrl" ng-init="init()">
<hr>
<label>Add an addon that is not bookable with this one:</label>
<input id="addon-search" type="text"
ng-model="addon_filter_name"
class="form-control" placeholder="Search for an addon...">
<br>
<div ng-show="data._embedded.exclusive_with && data._embedded.exclusive_with.length > 0">
<label>Not bookable with:</label>
<ul>
<li ng-repeat="exclusive_with in data._embedded.exclusive_with">
{{exclusive_with.description}} <a href="#"
ng-click="removeExclusivity($event, exclusive_with)"
title="Remove mutual exclusivity">
<span class="glyphicon glyphicon-trash"></span></a>
</li>
</ul>
</div>
</div>
And my controller contains the following code :
var lastFetchedAddonList = [];
$scope.init = function() {
$('#addon-search').autocomplete({
source: function( request, response ) {
Addon.query({ name: request.term, global: null }, function(results) {
lastFetchedAddonList = results.data;
var suggestions = [];
angular.forEach(results.data, function(v, i){
suggestions.push({ label: v.description, value: i });
});
response(suggestions);
});
},
select: function( event, ui ) {
event.preventDefault();
if ($scope.data._embedded === undefined) {
$scope.data._embedded = [];
}
if ($scope.data._embedded.exclusive_with === undefined) {
$scope.data._embedded.exclusive_with = [];
}
var addon = lastFetchedAddonList[ui.item.value];
var exclusiveAddon = new AddonExclusivity();
exclusiveAddon._id = $scope.data._id;
exclusiveAddon.exclusive_with_addon = addon.name;
AddonExclusivity.save(exclusiveAddon, function(){
$rootScope.addNotification('Added ' + addon.description + ' as mutually exclusive with ' + $scope.data.description);
$scope.data._embedded.exclusive_with.push(addon);
$('#addon-search').val('');
});
return false;
}
});
};
$scope.removeExclusivity = function($event, addon) {
$event.preventDefault();
AddonExclusivity.delete({ id: $scope.data._id, other_id: addon.name }, function(){
$rootScope.addNotification('Addon ' + addon.description + ' is not anymore mutually exclusive with ' + $scope.data.description);
$scope.data._embedded.exclusive_with.splice($scope.data._embedded.exclusive_with.indexOf(addon), 1);
});
};
Thank you for your help in advance :)
Instead of using jquery autocomplete, you can use plain html to create your autocomplete.
See this post here
I have the same problem. The reason is angularjs repeat ng-model tag the id is always the same.
So only the first tag with that id works fine with autocomplete.
but the second and after with same id would not work.
I am looking at angular-module angucomplete-alt angucomplete-alt
This should solve your problem.

AngularJs ng-repeat didn't updated after changing $scope variable

I have table with item list and modal window where i can change drug properties. When properties changed that drug have to remove from that list. But it's didn't remove.
Modal window:
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
var index = _.findIndex($scope.drugs, {_id: data._id});
$scope.drugs.splice(index,1);
}
i think element didn't remove 'cause when i open modal window i create copy of my scope and then work with copy..
On that form i have refresh button that refresh all list.
$scope.refresh= function() {
$scope.drugs = UnresolvedDrugsService.query();
};
and it's didn't work too. I think it happens because i work with copy too.
Okey, i try to emit some event
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
$rootScope.$emit('refreshDrug', index);
}
$rootScope.$on('refreshDrug', function(index){
$scope.drugs = [];
$scope.drugs.splice(index,1);
// $scope.drugs= UnresolvedDrugsService.query();
});
And it's not working.
Can you help me and describe what i doing wrong, thx!
UPD
modal window html
<form role="form" name="resolveDrugForm" ng-submit="saveResolvedDrug(drug) && $close(drug)">
........{some code, input's and label}......
<input type="submit" class="btn btn-primary" value="{{'COMMON.SAVE' | translate}}"/>
<button type="button" class="btn btn-default" ng-click="$dismiss()" >{{'COMMON.CANCEL' | translate}}</button>
code of ng-repeat
<tr ng-repeat="drug in drugs" ng-click="resolveDrug($index)">
<td>{{drug.productName || drug.description }}</td>
<td>{{drug.aiccode }}</td>
</tr>
and all method of controller:
$rootScope.$on('refreshDrug', function(index){
// $scope.drugs = [];
$scope.drugs.splice(index,1);
// $scope.drugs= UnresolvedDrugsService.query();
});
$scope.drugs= UnresolvedDrugsService.query();
$scope.refresh= function() {
$scope.drugs= UnresolvedDrugsService.query();
};
$scope.spliceEl = function(data){
var index = _.findIndex($scope.drugs, {_id: data._id});
$scope.drugs.splice(index,1);
return true;
};
$scope.saveResolvedDrug = function(drug){
DrugsService.addResolvedDrug(drug).then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
if(data.data.ingredients && data.data.ingredients.length > 0){
data.data.ingredients = JSON.parse(data.data.ingredients);
}
$scope.drugs.splice(index,1);
return true;
});
return true;
};
$scope.resolveDrug=function(index){
$scope.drug={};
var drugs = $scope.drugs;
$scope.drug=angular.copy($scope.drugs[index]);
var scope=$scope;
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
console.log($scope.drugs);
var index = _.findIndex($scope.drugs, {_id: data._id});
//$scope.drugs.splice(index,1);
console.log($scope.drugs);
$rootScope.$emit('refreshDrug', index);
}, function(data){
}).finally(function(data){
$scope.refresh();
});
}
I didn't know why it didn't works with events. But if saveDrug in modal result but sumbit form - work fine.
Now code looks like
$scope.resolveDrug=function(index){
$scope.drug={};
var drugs = $scope.drugs;
$scope.drug=angular.copy($scope.drugs[index]);
var scope=$scope;
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: scope,
resolve: {
drug: function () {
return $scope.drug;
}
}
}).result.then(function(data){
}, function(data){
}).finally(function(data){
});
}
$scope.saveResolvedDrug = function(drug){
DrugsService.addResolvedDrug(drug).then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
if(data.data.ingredients && data.data.ingredients.length > 0){
data.data.ingredients = JSON.parse(data.data.ingredients);
}
$scope.drugs.splice(index,1);
return true;
});
return true;
};

Resetting an input to initial value when a search result is clicked with AngularJS

I've a list on ng-repeat that displays a list of results from a $http query (bind to an input). I'd like both for the list to disappear when the user clicks on one of the results and for the initial empty value of the model to be restored.
Basically, the functionality is as follows:
User searches term, list displays results, user clicks on result, list disappears, user clicks on input again to make another search, list with new results appear.
So far I've managed to make the list disappear, but not to make it appear again when the user makes another search.
Here's the relevant code:
<input type="text" ng-model="name" ng-click="Research()"/>
<ul ng-hide="clicked" ng-show="retype">
<li ng-repeat="result in results" ng-click="getDetails(result.id)">{{result.title}}</li>
</ul>
And the JS:
function Ctrl($scope, $http) {
var get_results = function(name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=8').
success(function(data3) {
$scope.results = data3.results;
});
}
}
$scope.name = '';
$scope.$watch('name', get_results, true);
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=500').
success(function(data2) {
$scope.releases = data2.releases;
});
$scope.clicked = true;
}
function Research(){
$scope.retype = true,
$scope.name = '';
}
Plunkr is down, I'll make one as soon as possible. Any idea about what am I missing?
I tidied up your code a little bit. Please note that the div is shown only when artist is defined. So when it is set to undefined by the $scope.clear() method, the mentioned div is hidden.
Html part:
<div ng-controller="Ctrl">
<input type="text" ng-model="name" ng-focus="clear()"/>
<ul>
<li ng-repeat="result in results" ng-click="getDetails(result.id)">{{result.title}}</li>
</ul>
<div ng-show="artist">
<h1>Artist</h1>
<ul>
<li>{{artist.name}}</li>
<li>{{artist.release_url}}</li>
<li>{{artist.uri}}</li>
<li>{{artist.resource_url}}</li>
</ul>
</div>
</div>
JavaScript part:
var myApp = angular.module('myApp',[]);
function Ctrl($scope, $http) {
$scope.name = undefined;
$scope.artist = undefined;
$scope.results = undefined;
var search = function (name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=8').
success(function(data3) {
$scope.results = data3.results;
});
}
}
$scope.$watch('name', search, true);
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=500').
success(function(data2) {
$scope.releases = data2.releases;
});
}
$scope.clear = function () {
$scope.name = undefined;
$scope.artist = undefined;
$scope.results = undefined;
}
}
There is working JSFiddle.
Your Research function is unnecessary because you don't need ng-show and ng-hide same time...
secondly you set clicked to ok but never set it false again after your research done...
here is working PLUNKER
Try using just one ng-hide or ng-show, instead of both. Since you never set clicked back to false, it is probably overriding the retype.
Both functions are two-way, so you can just use ng-hide="clicked", and inside function Research, set $scope.clicked to false.

alternative method instead $watch in angularjs

I have a scope variable, when it returns true, i need to trigger some events or do something. I my case, the every first time, the scope variable returns undefined and later it returns true. In this case i used $watch method to get the expected funcionality. Is there any alternative approach to do the same instead using $watch ?
scope.$watch () ->
scope.initiateChild
, (value) ->
if value is true
$timeout ->
scope.buildOnboarding()
, 1000
You can try using AngularJS $on(), $emit() and $broadcast().
Here is an example: http://www.binaryintellect.net/articles/5d8be0b6-e294-457e-82b0-ba7cc10cae0e.aspx
You can use JavaScript getters and setters without any expense of using $watch.
Write code in the setter to do what you want when angular changes the your model's value you are using in scope. It gets null or an a State object as user types. Useful for working with type ahead text boxes that have dependencies on each other. Like list of counties after typing state without user selecting anything.
Here is some pseudo style code to get the idea.
<input ng-model="searchStuff.stateSearchText" />
<div>{{searchStuff.stateObject.counties.length}}</div>
<div>{{searchStuff.stateObject.population}}</div>
$scope.searchStuff=new function(){var me=this;};
$scope.searchStuff.stateObject = null;
$scope.searchStuff.getStateObjectFromSearchText = function(search){
// get set object from search then
return stateObject;
};
$scope.searchStuff._stateSearchText= "";
Object.defineProperty($scope.searchStuff, 'stateSearchText', {
get: function () {
return me._stateSearchText;
},
set: function (value) {
me,_stateSearchText = value;
me.stateObject = getStateObjectFromSearchText (value);
}
});
See this fiddle: http://jsfiddle.net/simpulton/XqDxG/
Also watch the following video: Communicating Between Controllers
A sample example is given below
Html:
<div ng-controller="ControllerZero">
<input ng-model="message" >
<button ng-click="handleClick(message);">LOG</button>
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
javascript:
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
function ControllerZero($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
ControllerZero.$inject = ['$scope', 'mySharedService'];
ControllerOne.$inject = ['$scope', 'mySharedService'];
ControllerTwo.$inject = ['$scope', 'mySharedService'];

Categories

Resources