My code is below, and I'm pretty new to angular. I have a controller that I want to call a webservice (already written by someone else). That call will happen after a user enters a sku and vendor number and clicks 'search.'
<div ng-controller="inventorySearchController">
<div class="container">
<tbody>
<tr>
<td class=""><input ng-model="skuField" type="text" my-maxlength="10" placeholder="Sku Number" data-ng-model=""/></td>
</tr>
<tr>
<td class=""><input ng-model="mVendorField" type="text" my-maxlength="10" placeholder="M-Vendor Number" data-ng-model=""/></td>
</tr>
<tr>
<td class=""><input type="button" class="btn btn-primary btn-lg" ng-click="inventorySearchController.callService()" value="Search" /></td>
</tr>
</tbody>
</div>
</div>
(function() {
'use strict';
angular.module('specificLoadApp').controller('inventorySearchController', inventorySearchController);
inventorySearchController.$inject = ['$scope','$http'];
function inventorySearchController($scope, $http) {
var callService = function(){
var urlSearchService = 'http://myorginaztion.com/services/search';
var skuVal = $scope.skuField;
var mVenVal = $scope.mVendorField;
var xml = "<ItemSearchRequest>"
+"<skuid>" + skuVal + "</skuid>"
+"<mvendor>" + mVenVal + "</mvendor>"
+"</ItemSearchRequest>";
console.log('calling: ' + urlSearchService);
$http.post(urlSearchService, xml).
success(function(data){
$scope.searchResults = data;
console.log('call to ' + urlSearchService + ", was a success.");
});
};
};
})();
It appears like callService is not bound to $scope. Try changing
var callService = function(){
to
$scope.callService = function(){
Since you're just getting started, avoid using $scope and instead use controller as syntax.
So in your div, where you load the controller, change it to:
<div ng-controller="inventorySearchController as inventorySearch">
And where you create the button change it to read:
<td class=""><input type="button" class="btn btn-primary btn-lg" ng-click="inventorySearch.callService()" value="Search" /></td>
Then lastly, in your controller, change you controller so that the first line reads: var vm=this;
and finally, change the function definition for call service to read:
vm.callService = function(){...}
The controller as syntax is generally considered a best practice and follows the guideline of "if you're not using a dot, you're doing it wrong."
You will also no longer need to inject $scope if you go this route.
Your bindings should be prefixed with inventorySearch as well if you make this change.
Related
I am attempting to follow a JSFiddle, where a user can click on a <td> item, edit it, then eventually be able to save the changes.
The example uses ng-repeat and all others I have looked at do to where as I am not, I am using data passed from a resolve command in my route folder.
$stateProvider
.state('app.patents.patent', {
url: '/{patentId}',
component: 'patent',
resolve: {
patent: ['patents', '$stateParams', function(patents, $stateParams) {
return patents.find(function(patent){
return patent.id == $stateParams.patentId;
})
}]
}
})
}]);
I have attempted to use data-id (looked at How to retrieve the clicked ElementId in angularjs?), but with no success, as I assume you cannot use the same id twice and my desired functionality requires two elements that ng-show and ng-hide depending on the boolean value passed to them.
I have now got myself in a confused state, not sure which approach to take.
Question
How do I adapt my code that doesn't use ng-repeat to work with this JSFiddle? OR do you know another apporach I can take to achieve the same results?
<tr>
<th class="text-xs-right">Short Name</th>
<td>
<span data-id="123" ng-hide="$ctrl.shortTitle.editing" ng-dblclick="$ctrl.editItem(123)">{{$ctrl.patent.shortTitle}}</span>
<input type="text" data-id="123" ng-show="$ctrl.shortTitles.editing" ng-blur="$ctrl.doneEditing(123)" ng-model="$ctrl.patent.shortTitle"></input>
</td>
</tr>
angular.module('myApp').component('patent', {
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm',
controller: function() {
var vm = this;
vm.editItem = function (item) {
item.editing = true;
}
vm.doneEditing = function (item) {
item.editing = false;
};
});
As per my understanding regarding your question I have created a jsfiddle, have a look or you can create a jsfiddle with the issue you are facing for better understanding
JSFiddle
<!DOCTYPE html>
<div ng-app ng-controller="myCtrl" class="container">Double-click on the items below to edit:
<button type="button" ng-click="newItem()">Add item</button>
<table>
<tr ng-repeat="item in items">
<td>
<span ng-hide="item.editing" ng-dblclick="editItem(item)">{{item.name}}</span>
<input ng-show="item.editing" ng-model="item.name" ng-blur="doneEditing(item)" autofocus />
</td>
</tr>
</table>
</div>
You can create an array and connect each input to a specific index starting from 0 and then pass that index to your function call.
<tr>
<th class="text-xs-right">Short Name</th>
<td>
<span ng-hide="$ctrl.editing[1]" ng-dblclick="$ctrl.editItem(1)">{{$ctrl.patent.shortTitle}}</span>
<input type="text" data-id="123" ng-show="$ctrl.editing[1]" ng-blur="$ctrl.doneEditing(1)" ng-model="$ctrl.patent.shortTitle"></input>
</td>
</tr>
angular.module('myApp').component('patent', {
templateUrl: 'p3sweb/app/components/patents/views/patent-item.htm',
controller: function() {
var vm = this;
vm.editing=[];
vm.editItem = function (index) {
vm.editing[index] = true;
}
vm.doneEditing = function (index) {
vm.editing[index] = false;
};
});
Demo: http://jsfiddle.net/F7K63/381/
Having trouble setting clearing input field
I’m currently trying to clear the input box on ng-model="newInstruction.instructionText" after an new instruction text has been added. Here is the code where I tried to reset it to empty string but input field didn't clear. Why is that?
I’ve tried console logging the following in updateInstructionText function:
Console.log (newInstruction) returns undefined in console.
Console.log ($scope.newInstruction) returns undefined in console.
Console.log (instruction) returns a object with instruction text inside ex: Object {instructionText: ‘new instruction’}
Controller:
angular.module('app').controller('InstructionController', function($scope, $http, NgTableParams, $filter) {
$scope.initList = function() {
$scope.getRemoteInstructions = function(typed) {
$http.get("/api/instructions?searchInstructionText=" + typed).then(function(response) {
$scope.instructionChoices = _.map(response.data, function(instruction) {
instruction.instructionText;
});
});
};
$scope.updateInstructionText = function(instruction) {
var instructionPromise;
if (instruction.id) {
instructionPromise = $http.put("api/instructions/" + instruction.id, instruction);
}
else {
instructionPromise = $http.post("/api/instructions", instruction);
}
$scope.newInstruction = '';
$instruction = '';
instructionPromise.then(function(response) {
$('#instructionModal').closeModal();
$scope.instructionTable.reload();
});
};
};
});
HTML
<div class="container-fluid" ng-init="initList()">
<div class="row">
<h3>Total Instructions: {{totalInstructions}}</h3>
<table class="striped highlight bordered" ng-table="instructionTable" show-filter="true">
<tr>
<td class="input-field">
<input placeholder="Enter New Instruction Text" ng-model="newInstruction.instructionText" type="text">
</td>
<td>
<a class="waves-effect waves-light btn" ng-click="updateInstructionText(newInstruction)">Add</a>
</td>
</tr>
</table>
</div>
</div>
<ng-include src="'/views/modals/instructionModal.html'"></ng-include>
</div>
Simply do:
instruction.instructionText = null
Since you are accessing the argument (object) inside the function with name instruction, if you set this to null, you can clear the input field
Simply do
$scope.newInstruction.instructionText=null
I have this input field in my html:
<input type="text" spellcheck="false" id="widgetu1049_input"
name="custom_U1049" tabindex="3" placeholder="Search..." ng-model="searchText"
ng-change="getPostHttp()" ng-trim="false"/>
and i'm calling http post in a scope function:
$scope.getPageItems = function(callback){//TODO add county and state moudles
var search = {'searchText':$scope.searchText,'state' : $scope.currentState,'county' : ''};
var params = {'action':'getPageItems', 'currentPage':$scope.currentPage, 'pageSize':$scope.pageSize, 'search':search };
$http.post(EndPoint, params).then(function(response) {
var page=response.data;
console.log(page);
callback(page);
});
}
I'm calling the above function from this function:
$scope.getPostHttp = function(){
$scope.getPageItems(function(data) {
$scope.items = data;
});
}
I've got this approach from this question Angular $http returns data but doesn't apply to scope
And although it shows the items on an ng-init call I made, it does not update on the ng-change call above.
Any ideas?
EDIT: I'm adding the view of the ng-repeat call:
<tr style=" background-color: #BFBFBF;" ng-model="items" ng-class="{marked: isExists(item.id) == true}" ng-click="view(item.id)"
data-toggle="modal" data-target="#smallModal" ng-repeat="item in items" ng-animate="'animate'">
<td ng-show="id">{{item.id}}</td>
<td ng-show="fname">{{item.fname}}</td>
</tr>
I am trying to create my first KnockoutJS form view in combination with Spring MVC's #ModelAttribute binding.
Data is loaded over Ajax and populated with KnockoutJS
Data is added over KnockoutJS
Data is removed over Ajax and KnockoutJS
Data will be saved with an normal POST submit to Spring MVC controller.
To bind the form inputs to a Spring MVC controller, I need the iteration index from KnockoutJS. So I tried following:
But the values from my database are never bound like they are when I am bind them with data-bind='value: key'. Can you help me, finding the mistake?
JSP:
<form:form modelAttribute="configurationHelper" action="/saveConfigurationList.htm" method="POST" id="configuration-form" class="form-inline">
<tbody data-bind="foreach: configurations">
<tr>
<td>
// this is working
<input data-bind='value: key' class="form-control input-sm" type="text"/>
// this is not working
<input data-bind='attr:{value: key, name:configurationHelper.configurations[$index].key' class="form-control input-sm" type="text"/>
</td>
<td>
<a href='#' data-bind='click: $root.removeConfiguration' class="ordinary-tooltip" title='<spring:message code="general.delete"/>'>
<i class="fa fa-lg fa-trash-o "></i>
</a>
</td>
</tr>
</tbody>
</form:form>
ModelView:
function ConfigurationViewModel() {
var self = this;
self.configurations = ko.observableArray([]);
self.loadConfigurations = function() {
$.ajax({
type : "POST",
url : "/loadConfigurationList.htm",
success : function(response) {
var responseArray = JSON.parse(response);
var mappedConfigurations = $.map(responseArray.configurations, function(configuration) {
return new Configuration(configuration);
});
self.configurations(mappedConfigurations);
},
error : function(e) {
alert('Error: ' + e.status);
}
});
}
self.saveConfigurationList = function() {
$("#configuration-form").submit();
}
self.addConfiguration = function() {
self.configurations.push({
id: 0,
key: "",
value: "",
});
};
self.removeConfiguration = function(configuration) {
if(confirm(springMessageGeneralDeleteReally)){
$.ajax({
type : "POST",
url : "/deleteConfiguration.htm",
data: {"configurationId": configuration.id},
success : function(response) {
self.configurations.remove(configuration);
},
error : function(e) {
alert('Error: ' + e.status);
}
});
}
};
}
function Configuration(data) {
this.id = ko.observable(data.id);
this.key = ko.observable(data.key);
this.value = ko.observable(data.value);
}
Summary:
Knockout should only take care of binding the values (loaded with AJAX) to the inputs and display the correct input-name. (to bind the input-value back to the Spring MVC controller)
configurationHelper is a request parameter and should not bother Knockout. It is only available to bind the list of configurationHelper.configurations to Spring MVC.
Following form is properly bound to Spring MVC controller:
<form:form modelAttribute="configurationHelper" action="/leina16/configuration/saveConfigurationList.htm" method="POST" id="configuration-form" class="form-inline">
<form:input path="configurations[0].key" class="form-control input-sm"/>
</form:form>
Now I want to extend inputs with Knockout JS so I need at least the data-bind attribute as well as the foreach: $index from Knockout:
<tbody data-bind="foreach: configurations">
<input data-bind='attr:{value: key, name:"configurations[$index].key}' class="form-control input-sm" type="text"/>
</tbody>
But the snipped above is neither bound to Spring MVC controller method nor the values are populated.
You have a missing } and are probably getting an error about Knockout being unable to parse bindings.
Change:
'attr:{value: key, name:configurationHelper.configurations[$index].key'
To:
'attr:{value: key, name:configurationHelper.configurations[$index].key}'
As configurationHelper is defined outside of your foreach loop, you'll need to reference this using $parent or $root:
'attr:{value: key, name:$parent.configurationHelper.configurations[$index].key}'
Solution:
Add quotes to "non-Knockout" elements and use $index() function.
<tbody data-bind="foreach: configurations">
<tr>
<td>
<input data-bind='attr:{value: key, name:"configurations["+$index()+"].key"}' class="form-control input-sm" type="text"/>
</td>
</tr>
</tbody>
I have a simple table app which gets JSON data from a database. It passes the data via parameter to my app controller, which then filters the data. This works great. However, it is a lot of data (hundred thousand objects). I have search boxes that I use to try and filter the data, and when the search watch should be getting called (when someone types something in the search box), it doesn't. Am I missing something?
js:
var app = angular.module('SortingTables', ['ui.bootstrap']);
//Dependencies which are services, providers or factories must map to string types, which are then passed into the instance function
app.filter('startFrom', function () {
return function (input, start) {
start = +start; //parse to int
return input.slice(start);
};
});
app.controller('Ctrl', function ($scope, filterFilter, dataTable) {
$scope.currentPage = 1;
$scope.itemsPerPage = 25;
$scope.totalItems = 0;
$scope.predicate = '';
$scope.searchBuffer = {
$: ''
};
$scope.filtered;
//This function has sort of been abstracted...
//The purpose of this function is to delay the update so the user gets a chance to finish typing before the filter is applied.
//Newer versions of angularjs have the ng-model-options: debounce=100 but we can't use that since we have IE 8 on dev boxes
$scope.$watch('searchBuffer', function (term) {
console.log('The watch on searchBuffer was called');
$scope.filtered = filterFilter(dataTable, term);
$scope.totalItems = $scope.filtered.length;
});
$scope.pageChanged = function () {
$scope.currentRow = $scope.currentPage * $scope.itemsPerPage - $scope.itemsPerPage;
};
});
html
<div ng-app="Components" ng-controller="Ctrl">
<hr/>
<table class="table table-striped">
<tr>
<th>Technical Owner
<br />
<input type="search" ng-model="searchBuffer['Technical Owner']">
</a>
</th>
<th>Branch
<br />
<input type="search" style="width: 40px" ng-model="searchBuffer.Branch">
</a>
</th>
<th>Sub Pillar
<br />
<input type="search" ng-model="searchBuffer['Sub Pillar']">
</a>
</th>
<th>Path
<br />
<input type="search" ng-model="searchBuffer.Path">
</a>
</th>
<th>Name
<br />
<input type="search" ng-model="searchBuffer.Name">
</a>
</th>
<th>Description
<br />
<input type="search" ng-model="searchBuffer.Description">
</a>
</th>
</tr>
<tr ng-repeat="ComponetOwner in filtered | startFrom:currentPage | orderBy:predicate:reverse | limitTo:itemsPerPage">
<td>{{ComponetOwner["Technical Owner"]}}</td>
<td>{{ComponetOwner.Branch}}</td>
<td>{{ComponetOwner["Sub Pillar"]}}</td>
<td>{{ComponetOwner.Path}}</td>
<td>{{ComponetOwner.Name}}</td>
<td>{{ComponetOwner.Description}}</td>
</tr>
</table>
<pagination items-per-page="itemsPerPage" total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()"></pagination>
</div>
When I type something in the search box, $watch doesn't get called. What's going on?
searchBuffer is an object. The third optional argument of $watch needs to be set to 'true' for watching objects/arrays (that is for deep watching).
Read this:
$watch an object
You can do $watchCollection which will watch all the objects within an object.
Not as deep as setting the third optional argument which is true for yout $watch function.
Here is a good blog about it: http://www.bennadel.com/blog/2566-scope-watch-vs-watchcollection-in-angularjs.htm