Trigger input event in angular - javascript

I have Angular directive. Something like multiselect with search.
I want to clear query after click on clear icon.
Here is the code:
Template:
<div multiselect>
<ul role="container">
<li ng-repeat="(k,v) in ::propertyModel.getAllowedValues()" ng-show="isSelected(k);">
{{v}}
<span class="icon-close" ng-click="handleOptionRemove(k);" ng-show="!singleSelect"></span>
</li>
</ul>
<div role="opener" class="icon-plus"></div>
<div role="dropdown" class="closed">
<div role="search">
<span class="icon-magnifier"></span>
<span class="icon-close" ng-click="handleSearchClear();"></span>
<input type="text" placeholder="Type to search">
</div>
<ul role="options">
<li ng-repeat="(k,v) in ::propertyModel.getAllowedValues()" ng-show="!isSelected(k) && found(k);" ng-click="handleOptionSelect(k);">{{v}}</li>
<li disabled ng-show="foundItems.length === 0 && queryString !== ''">There is no results found</li>
</ul>
</div>
</div>
Directive:
var input= element.find('input');
[...]
function handleSearchInput(){
scope.foundItems= [];
scope.queryString= input[0].value.toLocaleUpperCase();
for(var o in allowedValues) if(allowedValues.hasOwnProperty(o))
if(allowedValues[o].toLocaleUpperCase().indexOf(scope.queryString)!== -1)
scope.foundItems.push(o);
scope.$apply();
}
[...]
scope.handleSearchClear = function(){
input[0].value='';
input.triggerHandler('input');
};
[...]
input.bind('input', handleSearchInput);
After click i have
Error: [$rootScope:inprog] $apply already in progress[...]
on console.
How can i properly 'clear' this input's value?

Here's what I do in Jasmine tests to clear an element, perhaps this will be helpful:
var myInput = input[0]; // get the input somehow
angular.element(myInput).val('').trigger('input');
I do agree with tymeJV's suggestion to work from a model when possible. Then you'd end up with something like this:
$scope.model.myfieldval = '';
$scope.model.someOtherFieldVal = '';
$scope.form.myFormName.$setPristine();
Hope this is helpful.

Ok, it seems that i was jumped outside Angular context.
I was used handleSearchInput function in event callback fired by Angular itself and it was trigered with Angular's context, then i was forced this function with standard javascript context.
Or something like that ;)
Anyway there is a solution.
function handleSearchInput () {
scope.$apply(function () { //force Angular context (and scope)
doSearch();
});
}
function doSearch () {
scope.foundItems = [];
scope.queryString = input[0].value.toLocaleUpperCase();
for (var o in allowedValues) if (allowedValues.hasOwnProperty(o)) {
if (allowedValues[o].toLocaleUpperCase().indexOf(scope.queryString) !== -1) {
scope.foundItems.push(o);
}
}
}
scope.handleSearchClear = function () {
//always in context because of existing in scope
input[0].value = '';
doSearch();
};
input.bind('input', handleSearchInput);

Related

Filter data in ng-repeat to display only the clicked item with Angularjs

Please help a little bit.
I have a list of 7 events displayed already with Angularjs. I'd like when I click on the <h2> (the event name) of some event, to open an ovelay that displays the same data from the database but only for this event which is clicked.
I'm sure that 'filter' will do the work but it seems I'm doing something wrong.
Here is my code. The ng-app and ng-controller are in the <main> tag.
Angularjs version: 1.7.9
My Html:
<main ng-app="eventsApp" ng-controller="eventsCtrl">
<!-- Overlay that holds and displays a single event -->
<div>
<div ng-repeat="x in singlePageEvent | filter:hasName(x.eventName)">
<div>
<img ng-src="{{x.eventImgSrc}}" alt="{{x.eventImgName}}"/>
<h2 class="event-name">{{x.eventName}}</h2>
<p>{{x.eventTime}}</p>
<p>{{x.eventPlace}}</p>
</div>
</div>
</div>
<!-- A list with all the events -->
<div ng-repeat="x in events">
<div>
<img ng-src="{{x.eventImgSrc}}" alt="{{x.eventImgName}}"/>
<h2 ng-click="singleEventOpen(x)" class="event-name">{{x.eventName}}</h2>
<p>{{x.eventTime}}</p>
<p>{{x.eventPlace}}</p>
</div>
</div>
</main>
My script:
let eventsApp = angular.module('eventsApp', []);
this filter below is not working at all. It continues to show all the events.
eventsApp.filter('hasName', function() {
return function(events, evName) {
var filtered = [];
angular.forEach(events, function(ev) {
if (ev.eventName && ev.eventName.indexOf(evName) >-1) {
filtered.push(ev);
}
});
return filtered;
}
});
eventsApp.controller('eventsCtrl', function($scope, $http) {
let x = window.matchMedia("(max-width: 450px)");
let singleEventOverlay = angular.element(document.querySelector('div.single-event.overlay'));
let singleEvent = singleEventOverlay;
function responsiveEventImages(x) { //this displays the list with events
if (x.matches) {
$http.get('./includes/events_res.inc.php').then(function(response) {
$scope.events = response.data.events_data;
});
} else {
$http.get('./includes/events.inc.php').then(function(response) {
$scope.events = response.data.events_data;
});
}
}
...and then by invoking singleEventOpen() the overlay appears, but it displays all the data, not just the clicked event
$scope.singleEventOpen = function(singleEvent) {
let clickedEvent = singleEvent.eventName; //I got the value of each h2 click thanx to #georgeawg but now what?
console.log("Fetching info for ", singleEvent.eventName);
$http.get('./includes/single_event.inc.php').then(function(response) {
$scope.singlePageEvent = response.data.events_data;
});
singleEventOverlay.removeClass('single-event-close').addClass('single-event-open');
}
});
The php file with the database extraction is working fine so I won't display it here.
What should I do to make the overlay display only the event which <h2> is clicked?
Here is a pic of the list with events
Here is a pic of the overlay
Thanx in advance.
EDITED
I got the value of each h2 click thanx to #georgeawg but now what?
UPDATE
Hey, thanx a lot #georgeawg . After many attempts I finally did this:
$scope.singleEventOpen = function(singleEvent) {
$http.get('./includes/single_event.inc.php').then(function(response) {
let allEvents = response.data.events_data;
for (var i = 0; i < allEvents.length; i++) {
singleEvent = allEvents[i];
}
});
console.log('Fetching data for', singleEvent);
$scope.ex = singleEvent;
});
And it works well.
Change the ng-click to pass an argument to the singleEventOpen function:
<div ng-repeat="x in events">
<div>
<img ng-src="{{x.eventImgSrc}}" alt="{{x.eventImgName}}"/>
<h2 ng-click="singleEventOpen(x)" class="event-name">{{x.eventName}}</h2>
<p>{{x.eventTime}}</p>
<p>{{x.eventPlace}}</p>
</div>
</div>
Then use that argument:
$scope.singleEventOpen = function(singleEvent) {
console.log("Fetching info for ", singleEvent.eventName);
//...
//Fetch and filter the data
$scope.ex = "single item data";
}
Adding an argument is the key to knowing which <h2> element was clicked.
Update
Don't use ng-repeat in the overlay, just display the single item:
<!-- Overlay that holds and displays a single event -->
̶<̶d̶i̶v̶ ̶n̶g̶-̶r̶e̶p̶e̶a̶t̶=̶"̶x̶ ̶i̶n̶ ̶s̶i̶n̶g̶l̶e̶P̶a̶g̶e̶E̶v̶e̶n̶t̶ ̶|̶ ̶f̶i̶l̶t̶e̶r̶:̶h̶a̶s̶N̶a̶m̶e̶(̶x̶.̶e̶v̶e̶n̶t̶N̶a̶m̶e̶)̶"̶>̶
<div ng-if="ex"">
<div>
<img ng-src="{{ex.eventImgSrc}}" alt="{{ex.eventImgName}}"/>
<h2 class="event-name">{{ex.eventName}}</h2>
<p>{{ex.eventTime}}</p>
<p>{{ex.eventPlace}}</p>
</div>
</div>

Knockout.js -Getting error - Uncaught ReferenceError: Unable to process binding "with: function"

I am working on a neighborhood map project and I am stuck! I am new to knockout.js. I am trying to use data-bind getting this error -
knockout-3.4.1.js:72 Uncaught ReferenceError: Unable to process binding "with: function (){return filteredItems }"
The snippet of HTML source -
section class="main">
<form class="search" method="post" action="index.html" >
<input type="text" data-bind="textInput: filter" placeholder="Click here/Type the name of the place">
<ul data-bind="with: filteredItems">
<li><span data-bind="text: title, click: $parent.showInfoWindow"></span></li>
</ul>
</form>
</section>
and this is my viewModel -
function viewModel(markers) {
var self = this;
self.filter = ko.observable(''); // this is for the search box, takes value in it and searches for it in the array
self.items = ko.observableArray(locations); // we have made the array of locations into a ko.observableArray
// attributed to - http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html , filtering through array
self.filteredItems = ko.computed(function() {
var filter = self.filter().toLowerCase();
if (!filter) {
return self.items();
} else {
return ko.utils.arrayFilter(self.items(), function(id) {
return stringStartsWith(id.name.toLowerCase(), self.filter);
});
}
});
var stringStartsWith = function (string, startsWith) {
string = string || "";
if (startsWith.length > string.length)
return false;
return string.substring(0, startsWith.length) === startsWith;
};
// populateInfoWindow(self.filteredItems,)
// this.showInfoWindow = function(place) { // this should show the infowindow if any place on the list is clicked
// google.maps.event.trigger(place.marker, 'click');
// };
}
Some lines are commented because I am still working on it. To see the whole project- https://github.com/Krishna-D-Sahoo/frontend-nanodegree-neighborhood-map
The with binding creates a new binding context with the provided element. The error is thrown because of a reference to title within the <span> element, but filteredItems does not have a title property.
If you want to display a <li> element for each element in the filteredItems array, you can use a foreach binding, like this:
<ul data-bind="foreach: filteredItems">
<li><span data-bind="text: title, click: $parent.showInfoWindow"></span></li>
</ul>

Group Data and Display in Accordion using AngularJs and MVC

I am NEW to MVC as well as AngularJs and have been toiling over this for days. Although I feel that I am getting closer....still no cigar.
The problem: I have a list of reports that are grouped on the report type(name). I am trying to use an accordion to show and hide the list of reports in each group.
My controller.js looks like this (I know that it is wrong):
window.app.controller('relatedReportsController', ['$scope', '$timeout', 'relatedReportsService',
function ($scope, $timeout, relatedReportService) {
initialize();
function initialize()
{
$scope.relatedReports = [];
$scope.rollupVisible = false;
}
function sortOn(collection, name)
{
collection.sort(
function (a, b) {
if (a[name] <= b[name]) {
return (-1);
}
return (1);
});
}
$scope.groupBy = function (attribute) {
$scope.Groups = [];
sortOn($scope.relatedReports, attribute);
for (var i=0; i< $scope.relatedReports.length; i++)
{
var report = $scope.relatedReports[i];
}
}
$scope.toggleRollup = function($event)
{
if (angular.element($event.targe).hasClass('glyph')) return;
relatedReportService.$promise.then(function (data) {
$scope.relatedReports = data;
})
}
}]);
My page looks like this:
<li class="fruitRollup header row" ng-controller="relatedReportsController">
<div class="suitcaseheader">
<span class="col-xs-10 zero firstlabel">{{group.Name}}</span>
<span class="col-xs-3 zero datepad">Date</span>
<span class="floatR2">View</span>
<span class="clear"></span>
</div>
<div class="eaten">
<ul class="data">
#*#foreach (var reportResult in resultGroup.OrderByDescending(r=>r.Date))
{*#
<li class="data row" ng-repeat="report in group.reports" ng-controller="relatedReportsController">
<div class="suitcase">
<span class="col-xs-10 zero accountNumberColumn"></span>
#*<span class="middle zero">#reportResult.Date.Replace("12:00:00","")</span>*#
<span class="middle zero">{{report.Date}}</span>
<span class="floatR2">
<a class="icon-view glyph" target="_blank" href="#Url.ActionEncodedParameters("ViewDocument", "DocumentSearch", new { id = reportResult.Id })"></a>
</span>
<span class="clear"></span>
</div>
As you can see I need a lot of help. Thanks in advance!
The code that I had written in the controller.js was not properly getting the data. In trying to use a combination of samples that I found on the internet, I was thoroughly confused. I figured this out yesterday. I didn't need to get the data through the controller.js because the data was already being fetched through my page controller.cs and viewmodel. All I ended up needing was to use ng-show to show and hide the sections.
Thanks for taking the time to try to help.

Durandal 2.0 - Update the value of an observable in the view

I'm new to Durandal, my question has probably a very simple issue.
I load a list into a dropdown and the current value on the link which display the dropdown,
And the value of the link which display the dropdown is not updated correctly when an other value is selected.
But actually, I can't set the value of the observable in the select function.
View Model
var self = this;
self.system = require('durandal/system');
IPsKeys: ko.observableArray([]),
ipKeys: ko.observable(""),
activate: function (context) {
var that = this;
that.IPsKeys([]);
that.ipKeys("");
return $.when(
service.getIPSbyClientId(context.clientId).then(function (json) {
$.each(json, function (Index, Value) {
var ClientLobUWYear = {
NameLob: Value.LineOfBusiness.Name,
NameUWYear: Value.UnderwritingYear
};
that.IPsKeys().push(ClientLobUWYear);
// HERE MY VALUE IS GOOD UPDATING AND THE BINDING WORK
if (Index=== 0) {
that.ipKeys(ClientLobUWYear);
}
});
})
).then(function () {
//do some other datacontext calls for stuff used directly and only in view1
});
},
select: function (item) {
this.ipKeys = {
IdClient: item.IdClient,
IdLob: item.IdLob,
NameLob: item.NameLob,
NameUWYear: item.NameUWYear
};
/** PROBLEMS HERE **/
/** Uncaught TypeError: undefined is not a function **/
this.ipKeys(ClientLobUWYear);
},
View
<a id="select_lob-UWYear" class="dropdown-toggle" data-toggle="dropdown" href="#">
<span class="controls_value" data-bind="text: ipKeys().NameLob">ALOB</span>
<span class="controls_value" data-bind="text: ipKeys().NameUWYear">AYEAR</span>
</a>
<ul id="dropdown_year" class="dropdown-menu" data-bind="foreach: IPsKeys().sort(sortByLobYear)">
<li>
<a href="#" data-bind="click: $parent.select">
<span class="controls_value" data-bind="text: NameLob">Cargo</span>
<span class="controls_value" data-bind="text: NameUWYear">2014</span>
</a>
</li>
</ul>
Thanks a lot
The way you update an observable is like this:
var someObservable = ko.observable(""); //setting to "";
someObservable("Something else"); //updating to "Something else"
Not like this (which you are doing above)
var someObservable = ko.observable(""); //setting to "";
someObservable = "Something else";
This is overwriting someObservable with a string of value "Something else" and so is no longer an observable which is why it will not update the ui.
[JS Fiddle showing how to set observables.]

why does this knockout method receive a form element instead of the object its nested in?

I have this HTML:
<ul class="chat_list" data-bind="foreach: chats">
<li>
<div class="chat_response" data-bind="visible: CommentList().length == 0">
<form data-bind="submit: $root.addComment">
<input class="comment_field" placeholder="Comment…"
data-bind="value: NewCommentText"
/>
</form>
</div>
</li>
</ul>
and this JavaScript:
function ChatListViewModel(chats) {
// var self = this;
self.chats = ko.observableArray(ko.utils.arrayMap(chats, function (chat) {
return { CourseItemDescription: chat.CourseItemDescription,
CommentList: ko.observableArray(chat.CommentList),
CourseItemID: chat.CourseItemID,
UserName: chat.UserName,
ChatGroupNumber: chat.ChatGroupNumber,
ChatCount: chat.ChatCount,
NewCommentText: ko.observable("")
};
}));
self.newChatText = ko.observable();
self.addComment = function (chat) {
var newComment = { CourseItemDescription: chat.NewCommentText(),
ParentCourseItemID: chat.CourseItemID,
CourseID: $.CourseLogic.dataitem.CourseID,
AccountID: $.CourseLogic.dataitem.AccountID,
SystemObjectID: $.CourseLogic.dataitem.CommentSystemObjectID,
SystemObjectName: "Comments",
UserName: chat.UserName
};
chat.CommentList.push(newComment);
chat.NewCommentText("");
};
}
ko.applyBindings(new ChatListViewModel(initialData));
When I go into the debugger it shows that the chat parameter of the addComment() function is a form element instead of a chat object.
Why is this happening?
Because of KO behavior. To pass chat variable to submit handler you may use this:
<ul class="chat_list" data-bind="foreach: chats">
<li>
<div class="chat_response" data-bind="visible: CommentList().length == 0">
<form data-bind="submit: function(form){$root.addComment($data, form)}">
<input class="comment_field" placeholder="Comment…" data-bind="value: NewCommentText" />
</form>
</div>
</li>
</ul>
This is by design. From the Knockout.js docs:
As illustrated in this example, KO passes the form element as a
parameter to your submit handler function. You can ignore that
parameter if you want, but for an example of when it’s useful to have
a reference to that element, see the docs for the ko.postJson utility.
As noted by Serjio you can use currying to pass additional parameters into the function, or you can make use of Knockout's Unobtrusive Event Handling, which allows you to get the entire context associated with the form element.
self.addComment = function (form) {
var context = ko.contextFor(form);
var chat = context.$data;
//rest of your method here
};

Categories