Please ask me for better explanation. I have built a global search function into the header of my site. I want to display a separate input box for mobile search that uses the same ng-click event but the input isn't displayed when the page loads. I am having trouble getting the hidden input value on the mobile ng-click once it is displayed.
The areas of concentration are the search click function is not finding the correct ng-model when the function is triggered. I think it is because since the hidden elements are not available on load the ng-model="searchQueryStringMobile" isn't applied to the scope somehow.
My question is how do I get ng-model="searchQueryStringMobile" applied in the scope after it has been displayed posthumously or post-click ng-click="flipNav('search')" so that it does not return undefined when you activate the ng-click="loadSearchResults"?
Here is the code:
HTML:
<div ng-controller="HeaderCtrl as header" class="container">
<div id="jesusSearchTop">
<input ng-model="searchQueryString" class="jesusSearchInput autoCompSearch" type="search" placeholder="Search..." autocomplete="off" />
<select ng-model="searchDDL.item" class="jesusSearchSelect" ng-options="item.name for item in searchDDL track by item.id"></select>
<div class="jesusSearchHolder">
<img class="goSearch" ng-model="jesusSearch" ng-click="loadSearchResults('norm')" src="/EMR4/img/gui_icons/searchIcon.png" alt="Search EMR" />
</div>
</div>
<div id="siteControls">
<div id="siteSearch" class="siteControl" ng-click="flipNav('search')"></div>
</div>
<div ng-switch="dd" class="dropDown">
<div ng-switch-when="none" style="display:none"></div>
<div ng-switch-when="search" class="dropMenu listStyle4" id="Search">
<input ng-model="searchQueryStringMobile" class="jesusSearchInput" type="text" placeholder="Search..." autocomplete="off" />
<select ng-model="searchDDL.item" class="jesusSearchSelect" ng-options="item.name for item in searchDDL track by item.id"></select>
<div class="jesusSearchHolder">
<img class="goSearch" ng-model="jesusSearchMobile" ng-click="loadSearchResults('mob')" src="/EMR4/img/gui_icons/searchIcon.png" alt="Search EMR" />
</div>
</div>
</div>
<div class="clr"></div>
</div>
Controller:
app.controller('HeaderCtrl', function ($scope, $http, $location, populateDDL) {
$http.get(badge.credentials[7].home+'data.JSON')
.success(function(data, status, headers, config) {
$scope.header = data.header;
$scope.searchOptions = new populateDDL('tblWebMenuItems',badge.credentials[1].key).
then(function(response) {
$scope.searchDDL = response.tblWebMenuItems
$scope.searchDDL.item = $scope.searchDDL[0];
});
})
.error(function(data, status, headers, config) {
console.log(data+', '+status+', '+headers+', '+config);
});
$scope.flipNav = function(choice){
if ($scope.dd === choice) {
console.log(choice);
$scope.dd = "none";
}else {
$scope.dd = choice;
}
};
$scope.loadSearchResults = function(uv) {
var loader;
if (uv === "mob") {
loader = $scope.searchQueryStringMobile;
}else if (uv === "norm") {
loader = $scope.searchQueryString;
}
console.log(uv+' - '+loader);
if (loader == null || loader < 2) {
alert('Please refine your search and continue, Thank you!');
}else {
$location.path("/search/"+$scope.searchDDL.item.name.toLowerCase()+"/");
$location.search("type",$scope.searchDDL.item.name.toLowerCase());
$location.search("query", loader);
}
};
});
i have tested your code and found that it is because of the ng-switch.As ng-switch creates its own new scope which is child scope of its parent's, so if you use ng-model=$parent.searchQueryStringMobile , then it will work fine or If you use ng-show instead of ng-swtich ,it will work because ng-show doesnt create new child scope, it just sets the markup's css property display to noneand $parent allows you to access items of parent scope from child scope.In your example, $scope.searchQueryStringMobile is in the parent scope of ng-switch's scope. Here is the working plunk click
you can change your ng-switch markup to this
<div ng-switch="dd" class="dropDown" >
<div ng-switch-when="none" style="display:none"></div>
<div ng-switch-when="search" class="dropMenu listStyle4" id="Search">
<input ng-model="$parent.searchQueryStringMobile" class="jesusSearchInput" type="text" placeholder="Search..." autocomplete="off" />
<select ng-model="searchDDL.item" class="jesusSearchSelect" ng-options="item.name for item in searchDDL track by item.id"></select>
<div class="jesusSearchHolder">
<img class="goSearch" ng-model="jesusSearchMobile" ng-click="loadSearchResults('mob')" src="/EMR4/img/gui_icons/searchIcon.png" alt="Search EMR" />
</div>
</div>
</div>
Notice the ng-model for the input element in the above code.
Your problem is quite simple one. ng-switch just like ng-if creates new scope, so when you are using ng-model, yu are assign property to this new scope and not the scope used by your controller.
Solution would be to use controller as syntax or use property of some object created on scope. To illustrate this I create example for you.
As you can see {{a}} does not work outside new scope, but {{x.b}} works just fine.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-init="x = {}; show = 'first'">
<button type="button" ng-click="show = 'first'">show first</button><br>
<button type="button" ng-click="show = 'second'">show second</button><br>
a = {{a}}<br>
x.b = {{x.b}}
<div ng-switch="show">
<div ng-switch-when="first">
<input type="text" ng-model="a">
</div>
<div ng-switch-when="second">
<input type="text" ng-model="x.b">
</div>
</div>
</div>
Related
I know there are a ton of questions already on this topic, I tried the solutions I found but it doesnt seem to work.
Basically I have this directive called basicMunch that goes through an array of objects and creates some html.
Everything works nicely and bind other than the ng-modal
Here is the code for the directive:
> munches.directive('basicmunches', function() { return {
> require: 'ngModel',
> template:`<div class='columns'> <div ng-repeat="mu in basicMunches" class="card column col-4 col-sm-12">
> <div class="card-header">
> <h4 class="card-title">{{mu.name}}</h4>
> <h6 class="card-subtitle">{{mu.type}}</h6>
> </div>
> <div class="card-body">
> {{mu.text}}
> </div>
> <div class="card-footer">
> <div class="form-group">
> <label class="form-switch">
> <input ng-model="mu.tag" name="{{mu.tag}}" type="checkbox" />
> <i class="form-icon"></i> Turn On
> </label>
> </div>
> </div>
> </div></div>` } });
Here is the array:
$scope.basicMunches = [
{"name":"The New York TImes",
"text":"Get the top headlines from the NYT every morning",
"type":"News", "tag":"nyt"
},
{"name":"Wall Street Journal",
"text":"Get the top headlines from the WSJ every morning",
"type":"News", "tag":"wsj"
}
];
Here is what i see in the console
I have tried doing $parent.mu.tag and $parent.$parent.mu.tag, but that didnt work as it shouldnt have because it isnt nested in some other scope (atleast not that I know of)
I tried doing the name of the controller.mu.tag, that too doesnt work
I tried doing mu['tag'] and that doesnt work either
I tried using {{ and that doesnt work
I changed it to ng-bind={{mu.tag}} and that does work
It is weird to me that it works for ng-bind but it doesnt work for ng-model....
Anyhow, anyone have any ideas?
It seems like what you are wanting is to bind the ng-model property to the value of mu.tag, rather than to mu.tag itself.
Due to the way that ng-model is parsed, you need to use a variation of the bracket syntax in order to make this possible. The proper syntax here is $parent[mu.tag] which will look on the $parent object for the property named by the value of mu.tag. The parent of the ng-repeat is $scope, so this ends up with the property on the correct object.
<input ng-model="$parent[mu.tag]" name="{{mu.tag}}" type="checkbox" />
http://plnkr.co/edit/RZNDa2XVUoZp1z7QeN46?p=preview
Have the checkbox inputs fill properties of an object with property accessor bracket notation:
<fieldset ng-repeat="mu in munchies">
<input ng-model="choices[mu.tag]" name="{{mu.tag}}"
type="checkbox" />
Turn On
</fieldset>
The DEMO
angular.module("app",[])
.controller("ctrl",function($scope) {
var vm = $scope;
vm.choices = {};
vm.munchies = [
{"name":"The New York TImes",
"text":"Get the top headlines from the NYT every morning",
"type":"News", "tag":"nyt"
},
{"name":"Wall Street Journal",
"text":"Get the top headlines from the WSJ every morning",
"type":"News", "tag":"wsj"
}
];
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl">
Subscriptions {{choices | json}}
<fieldset ng-repeat="mu in munchies">
<h3>{{mu.name}}</h3>
<p>{{mu.text}}</p>
<input ng-model="choices[mu.tag]" name="{{mu.tag}}"
type="checkbox" />
Turn On
<br/>Subscribed {{choices[mu.tag]}}
</fieldset>
</body>
I have an array of objects which i populate on a button click.
When populating this array i make sure that i only add 10 objects to it.
When this is all loaded in the dom i give the user the oppertunity to add a few more objects.
I do this like this:
$scope.Information = [];
$.each(data, function (i, v) {
if (i<= 9)
$scope.Information.push(data[i]);
if(i >= 10) {
cookieList.push(data[i]);
}
}
if (cookieList.length) {
localStorage.setItem("toDoList", JSON.stringify(cookieList));
$(".showMore").removeClass("hidden");
}
$(".showMore").on("click", function() {
var obj = JSON.parse(localStorage.getItem("toDoList"));
console.log(obj);
console.log(obj.length);
SetSpinner('show');
$scope.Information.push(obj);
SetSpinner('hide');
//$.removeCookie("toDoList2");
});
part of the HTML:
<div ng-repeat="info in Information" class="apartment container" style="padding-right:35px !important">
<div class="row" style="height:100%">
<div class="col-md-1 col-xs-12">
<div>
<h4 class="toDoListHeadings">Nummer</h4>
<div style="margin-top: -15px; width:100%">
<span class="toDoListItems number">
{{info.orderraderid}}
</span>
</div>
</div>
</div>
</div>
</div>
My issue: When i add objects to my array of objects "$scope.Information.push(obj);" I assumed that they would get added in the DOM but they do not, how do i do this the angular way?
EDIT MY SOLOUTION:
edited the HTML to use ng-click and the method is as follows:
$scope.addMore = function() {
var obj = JSON.parse(localStorage.getItem("toDoList"));
SetSpinner('show');
$.each(obj, function(i,v) {
$scope.Information.push(v);
});
SetSpinner('hide');
}
Here is the angular way:
The view
<!-- Reference your `myapp` module -->
<body data-ng-app="myapp">
<!-- Reference `InfoController` to control this DOM element and its children -->
<section data-ng-controller="InfoController">
<!-- Use `ng-click` directive to call the `$scope.showMore` method binded from the controller -->
<!-- Use `ng-show` directive to show the button if `$scope.showMoreButton` is true, else hide it -->
<button data-ng-click="showMore()" data-ng-show="showMoreButton">
Show more
</button>
<div ng-repeat="info in Information" class="apartment container" style="padding-right:35px !important">
<div class="row" style="height:100%">
<div class="col-md-1 col-xs-12">
<div>
<h4 class="toDoListHeadings">Nummer</h4>
<div style="margin-top: -15px; width:100%">
<span class="toDoListItems number">
{{info.orderraderid}}
</span>
</div>
</div>
</div>
</div>
</div>
</section>
</body>
The module and controller
// defining angular application main module
var app = angular.module('myapp',[])
// defining a controller in this module
// injecting $scope service to the controller for data binding with the html view
// (in the DOM element surrounded by ng-controller directive)
app.controller('InfoController',function($scope){
$scope.Information = [];
$scope.showMoreButton = false;
// Bind controller method to the $scope instead of $(".showMore").on("click", function() {});
$scope.showMore = function(){
var obj = JSON.parse(localStorage.getItem("toDoList"));
console.log(obj);
console.log(obj.length);
SetSpinner('show');
$scope.Information.push(obj);
SetSpinner('hide');
//$.removeCookie("toDoList2");
};
$.each(data, function (i, v) {
if (i<= 9) $scope.Information.push(data[i]);
if(i >= 10) cookieList.push(data[i]);
});
if (cookieList.length) {
localStorage.setItem("toDoList", JSON.stringify(cookieList));
//$(".showMore").removeClass("hidden");
$scope.showMoreButton = true; // use $scope vars and ng-class directive instead of $(".xyz").blahBlah()
}
});
You should not use JQuery, use ng-click to detect the click, because angular has no idea when JQuery is done and when it needs to refresh the interface
So the situation is as follows:
I have an input bar where a user can search up a business name or add a person's name (and button to select either choice). Upon hitting enter I want to append a unique instance of a template (with the information entered by the user added). I have 2 templates I've created depending of if the user is searching for a business or a person.
One approach I've thought about is creating an object with the data and adding it with ng-repeat, however I can't seem to get the data loaded, and even then don't know how I can store reference to a template in my collection.
The other idea I've come across is adding a custom directive. But even then I've yet to see an example where someone keeps appending a new instance of a template with different data.
Here is the code so far:
payments.js:
angular.module('payment-App.payments',['ngAutocomplete'])
.controller('paymentController', function($scope, $templateRequest/*, $cookieStore*/) {
$scope.businessID;
$scope.address;
$scope.isBusiness = false;
$scope.payees = [];
$scope.newPayee = function () {
this.businessID = $scope.businessID;
this.address = $scope.address;
}
$scope.submit = function () {
var text = document.getElementById("businessID").value.split(",");
$scope.businessID = text[0];
$scope.address = text.slice(1).join("");
$scope.newPayee();
}
$scope.addPayee = function () {
$scope.submit();
$scope.payees.push(new $scope.newPayee());
console.log($scope.payees);
}
$scope.selectBusiness = function () {
//turns on autocomplete;
$scope.isBusiness = true;
}
$scope.selectPerson = function () {
//turns off autocomplete
$scope.isBusiness = false;
}
$scope.fillAddress = function () {
// body...
}
})
.directive("topbar", function(){
return {
restrict: "A",
templateUrl: 'templates/businessTemplate.html',
replace: true,
transclude: false,
scope: {
businessID: '=topbar'
}
}
})
payments.html
<h1>Payments</h1>
<section ng-controller="paymentController">
<div>
<div class="ui center aligned grid">
<div class="ui buttons">
<button class="ui button" ng-click="selectBusiness()">Business</button>
<button class="ui button arrow" ng-click="selectPerson()">Person</button>
</div>
<div class="ui input" ng-keypress="submit()">
<input id="businessID" type="text" ng-autocomplete ng-model="autocomplete">
</div>
<div class="submit">
<button class="ui button" id="submit" ng-click="addPayee()">
<i class="arrow right icon"></i>
</button>
</div>
</div>
<div class="search"></div>
<div class="payments" ng-controller="paymentController">
<li ng-repeat="newPayee in payees">{{payees}}</li>
</div>
<!-- <topbar></topbar> -->
</div>
(example template)
businessTemplate.html:
<div class="Business">
<div class="BusinessName" id="name">{{businessID}}</div>
<div class="Address" id="address">{{address}}</div>
<button class="ui icon button" id="hoverbox">
<i class="dollar icon"></i>
</button>
</div>
I ended up using a workaround with ng-repeat here. Still curious about the original question though.
I'm trying to make my first AngularJS application and I've run into a problem.
I have an input:
<input ng-model="userNameLogin" type="text" placeholder="username" class="form-control">
A button:
<button ng-click="setActiveUser()" type="submit" class="btn btn-success">Sign in</button>
and an expression:
{{ activeUser }}
I want the text to change to whatever was typed in the input once the button is clicked. For that I have the following controller:
app.controller('View1Ctrl', ['$scope', function($scope) {
$scope.userNameLogin = "";
$scope.activeUser = "Test";
$scope.setActiveUser = function() {
$scope.activeUser = $scope.userNameLogin;
console.log($scope.activeUser);
};
}]);
The initial value "Test" is shown just fine and according to the console the value of "activeUser" is being changed correctly as well. But the text in the view stays the same.
I have seen similar questions where a $scope.$apply() was the answer, but if I add that after the console.log I get
"Error: [$rootScope:inprog] $apply already in progress".
What am I missing here?
EDIT:
I have noticed that If I put the input, button and expression in the same HTML file it all works fine. However my Input and button are in a navbar in index.html while the expression is in view1.html
This is the body of index.html:
<body ng-app="myApp.view1">
<nav class="navbar navbar-inverse navbar-fixed-top" ng-controller="View1Ctrl as view">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#/view1">Kwetter</a>
</div>
<div class="navbar-collapse collapse" >
<ul class="nav navbar-nav">
<li>Home</li>
<li>Profile</li>
</ul>
<form class="navbar-form navbar-right">
<div class="form-group">
<input ng-model="userNameLogin" type="text" placeholder="username" class="form-control">
</div>
<div class="form-group">
<input type="password" placeholder="password" class="form-control">
</div>
<button ng-click="setActiveUser()" type="submit" class="btn btn-success">Sign in</button>
</form>
</div>
</div>
</nav>
<div id="pagewrapper" class="container">
<div ng-view></div>
<div>Angular seed app: v<span app-version></span></div>
</div>
and this is my view1.html
<div ng-controller="View1Ctrl as view">
<!-- row 1: welcome -->
<div class="row">
<div class="col-md-12 pull-left">
<image ng-src="{{ view.users[0].avatar }}"/>
<!-- If I put the button and input here it will work -->
<input ng-model="userNameLogin" type="text" placeholder="username" class="form-control">
<button ng-click="setActiveUser()" type="submit" class="btn btn-success">Sign in</button>
{{ activeUser }}
</div>
</div>
<!-- row 2: main content -->
<!-- left the rest of the content out because it would just clutter the page -->
I tried placing the ng-controller in <div id="pagewrapper" class="container"> instead of the first div of view1.html, but that made no difference.
I think u have misplaced the button or textbox or expression,
note : these should be inside the ng-controller.
please try this, it will work
<html>
<head>
<script data-require="angular.js#*" data-semver="1.4.0-beta.6" src="https://code.angularjs.org/1.4.0-beta.6/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app">
<div ng-controller="View1Ctrl">
<input ng-model="userNameLogin" type="text" placeholder="username" class="form-control">
<button ng-click="setActiveUser()" type="submit" class="btn btn-success">Sign in</button>
{{activeUser}}
</div>
<h1>Hello Plunker!</h1>
</body>
</html>
script.js code
var app = angular.module("app",[]);
app.controller('View1Ctrl', ['$scope', function($scope) {
$scope.userNameLogin = "";
$scope.activeUser = "Test";
$scope.setActiveUser = function() {
$scope.activeUser = $scope.userNameLogin;
console.log($scope.activeUser);
};
}]);
refer http://plnkr.co/edit/ixbByBQ9nGm4XEqEFi4t?p=preview
You have the properties directly on $scope and that is breaking the binding. Instead try:
app.controller('View1Ctrl', ['$scope', function($scope) {
$scope.userInfo = {
userNameLogin: "",
activeUser:"Test"
}
$scope.setActiveUser = function() {
$scope.uesrInfo.activeUser = $scope.userInfo.userNameLogin;
console.log($scope.activeUser);
};
}]);
and in your view:
{{userInfo.activeUser}}
From Egghead.io https://egghead.io/lessons/angularjs-the-dot
Within your code I can't see anything causing the problem. I made a fiddle, that shows that your code works:
http://jsfiddle.net/xxvsn8xs/
You need to declare the ng-appand the ng-controller of course, like in the fiddle, to let the app work at all.
Also, an view update might not occur, if setting the activeUser actually occurs outside of the angular scope, which might be within an external library or whatever. It is true, that these could be achieved by calling $scope.$apply() directly, but it is nor recommended, as the digest might already be in progress. This is the case in your code, as why you get the according error message.
Instead use angulars $timeout service with a callback and 0 delay, that applies the value to $scope.activeUser. $timeout will check, if a digest cycle is in progress and if not, will start one.
$scope.setActiveUser = function() {
$timeout(function () {
$scope.activeUser = $scope.userNameLogin;
console.log($scope.activeUser);
});
};
Don't forget to define $timeout in your controllers dependencies:
app.controller('View1Ctrl', ['$scope', '$timeout', function($scope, $timeout) {
Angular watches the variable you bind to $scope, but if you replace that variable Angular is not able to detect it. That's why $apply would be a suggestion.
Another suggestion is to bind the variable to a 'model' variable:
app.controller('View1Ctrl', ['$scope', function($scope) {
$scope.userNameLogin = "";
$scope.myData = { activeUser: "Test" };
$scope.setActiveUser = function() {
// Angular will pick up the change in the myData object, and will update all variables attached to it
$scope.myData.activeUser = $scope.userNameLogin;
console.log($scope.myData.activeUser);
};
}]);
view:
{{ myData.activeUser }}
Do you execute your application in Apache ? I'd the same issue when I was using file:// And I fixed my issue by using a localhost.
I put my navbar (containing the input and button) in a partial and made a new directive for it. Instead of placing the navbar in the index.html I put it in the individual partials and now it works fine. I suspect the problem had something to do with different scopes.
navbar html:
<a class="navbar-brand" href="#/view1">
Kwetter
<image id="navbar-image" src="src/kwetter_logo.png"/>
</a>
</div>
<div class="navbar-collapse collapse" >
<ul class="nav navbar-nav">
<li>Home</li>
<li>Profile</li>
</ul>
<form class="navbar-form navbar-right">
<div class="form-group">
<input ng-model="userNameLogin" type="text" placeholder="username" class="form-control">
</div>
<div class="form-group">
<input type="password" placeholder="password" class="form-control">
</div>
<button ng-click="setActiveUser()" type="submit" class="btn btn-success">Sign in</button>
</form>
</div>
</div>
the directive:
app.directive('navbar', function() {
return {
restrict: 'E',
templateUrl: 'partials/navbar.html',
controller: 'View1Ctrl as view'
}
});
Then I just added <navbar></navbar> to every view where I want a navbar.
Thanks everyone, your help pushed me in the right direction.
I'm quite frustrated and apologize in advance for poorly formulated question.
I've created derictive for simple list editing:
angular.module('myApp').
directive('variableList', function () {
return {
restrict: 'AE',
templateUrl: 'variableList.html',
replace: true,
scope: {
value: '='
},
controller: [
'$scope', '$element', '$attrs', '$transclude',
function($scope) {
$scope.removeListItem = function (index) {
$scope.value.splice(index, 1);
};
$scope.addListItem = function () {
$scope.value.push($scope.nextListItem);
$scope.nextListItem = null;
};
}
]
};
});
and template
<div class="variable-list">
<div class="variable-list-items">
<div class="row collapse variable-list-item" ng-repeat="(index, val) in value">
<div class="small-11 columns variable-list-item-value">
<input type="text" ng-model="val" />
</div>
<div class="small-1 columns">
<button class="button alert prefix no-margin icon-minus"
ng-click="removeListItem(index)"></button>
</div>
</div>
</div>
<div class="row collapse variable-list-controls">
<div class="small-11 columns">
<input type="text" ng-model="nextListItem" />
</div>
<div class="small-1 columns">
<button ng-class="{disabled: !nextListItem}"
ng-click="addListItem()"
class="button success prefix no-margin icon-plus"></button>
</div>
</div>
</div>
the important part of template if
<input type="text" ng-model="val" />
In the end I have quite working ui
But inputs for existings items doesnt work! Nothing happen when I try to edit them. Input for new item, add and remove buttons works as intended.
Any ideas?
Edit
I've tried to bind model like this
<input type="text" ng-model="value[key]" />
I was able to edit input but it caused even more magic, after first keypress input loses focus.
Found answer here https://github.com/angular/angular.js/issues/1267
Basically you have to have a . in ng-model or the revers data binding does not work on primitives.