knockout template - binding text to a function, with template data passed in - javascript

i have a view model with an observable array. Its populated with some json:
this.socialTiles = ko.observableArray([]);
ko.computed(function () {
jQuery.getJSON( this.apiURL+"&callback=?", function (data) {
var theData = data.entries;
tilesModel.socialTiles(theData);
console.dir(theData);
});
}, tilesModel);
for each item in the model, i build an li using template:
<ul id="tiles-ul" data-bind="template: {name:'twitter_template', foreach:socialTiles}">
<script type="text/html" id="twitter_template">
<li class="social-tile box-shadow">
<div class="header">
<div class="header-img">
<img data-bind="attr: { src: actor.avatar}">
</div>
<div class="name_and_time">
<span class="full-name" data-bind="text: actor.title"></span>
<span class="twitter-name" data-bind="text: actor.id"></span>
<span class="how-long-ago" > 5 minutes ago </span>
</div>
</div>
<div class="message-content" data-bind="html: object.content">
</div>
<div class="footer">
<div class="social-icon-twitter">
</div>
</div>
<span data-bind="text: $index"></span>
</li>
</script>
id like to data-bind the text of an element to be a result of a function, with the current data from the model used as an argument . example:
actor.id is a string containing a twitter url of a user (like "http://twitter.com/iamdiddy")
i'd like to pass that string to a function and return a "#iamdiddy" representation.
<span class="twitter-name" data-bind="text: getTwitterTag(actor.id)"></span>
in the view model
function getTwitterTag("twURL"){
return ... whatever;
}
how can I do this (call a function with argument, not extract the #... )? Does knockout support this functionality?

Try changing
<span class="twitter-name" data-bind="text: getTwitterTag(actor.id)"></span>
to
<span class="twitter-name" data-bind="text: $root.getTwitterTag($data)"></span>

Related

Angular - loop over nested javascript arrays

How can I use ng-repeat to loop over data that contains many nested array data?
I have data that will have many "Segments"
Example:
confirm.booking.flightData[0].Segments[0].FlightNumber
confirm.booking.flightData[0].Segments[1].FlightNumber
confirm.booking.flightData[0].Segments[2].FlightNumber
I have done both ng-repeat with angular, and without angular I would end up resorting to javascript that loops over data and creates the html dynamically, but I wish to do this the ANGULAR way.. HOW?
HTML with Angular/Javascript Arrays:
<div class="container-fluid">
<div class="row">
<div class="col-md-4">
<span style="font-weight: bold;">Flight</span>
</div>
<div class="col-md-4">
<span style="font-weight: bold;">Departs</span>
</div>
<div class="col-md-4">
<span style="font-weight: bold;">Arrives</span>
</div>
</div>
<div class="row">
<div class="col-md-4">
{{confirm.booking.flightData[0].Segments[0].FlightNumber}}
</div>
<div class="col-md-4">
({{confirm.booking.flightData[0].Segments[0].DepartureAirport}})
</div>
<div class="col-md-4">
({{confirm.booking.flightData[0].Segments[0].ArrivalAirport}})
</div>
</div>
</div>
Nesting can be done in repeats, but repeating too much in ng-repeats can be costly in terms of performance as angular creates scopes for each element repeated. Hence, filtering data till the perfect abstracted values that you need in terms of html should be done in the js file.
For eg: if u need only segements in the html form do this, or if u need even flight data in html form follow #Rachel's post
<ul data-ng-repeat="item in confirm.booking.flightData[0].Segments">
<li>{{ item.FlightNumber}}</li>
</ul>
Let's say your data is in flightdetails, then you can go about it like this:
<div ng-repeat="a in flightdetails ">
<div ng-repeat="b in a.booking">
<div ng-repeat="c in b.flightdata">
<div ng-repeat="d in c.segments">
{{d.flightnumber}}
</div>
</div>
</div>
</div>
You can use nested ng-repeat to bind your data - see a demo below:
angular.module("app", []).controller("ctrl", function($scope) {
$scope.confirm = {
booking: {
flightData: [{
Segments: [{
FlightNumber: 1
}, {
FlightNumber: 2
}]
}, {
Segments: [{
FlightNumber: 3
}, {
FlightNumber: 4
}]
}]
}
}
// console.log($scope.confirm);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="wrapper" ng-app="app" ng-controller="ctrl">
<div ng-repeat="x in confirm.booking.flightData">
Data {{$index + 1}}:
<div ng-repeat="y in x.Segments">
<div>Flight No: {{y.FlightNumber}}</div>
</div>
<br/>
</div>
</div>
If you want to display only the following:
confirm.booking.flightData[0].Segments[0].FlightNumber
confirm.booking.flightData[0].Segments[1].FlightNumber
confirm.booking.flightData[0].Segments[2].FlightNumber
then you can use limitTo - see demo below:
angular.module("app", []).controller("ctrl", function($scope) {
$scope.confirm = {
booking: {
flightData: [{
Segments: [{
FlightNumber: 1
}, {
FlightNumber: 2
}]
}, {
Segments: [{
FlightNumber: 3
}, {
FlightNumber: 4
}]
}]
}
}
// console.log($scope.confirm);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="wrapper" ng-app="app" ng-controller="ctrl">
<div ng-repeat="x in confirm.booking.flightData | limitTo : 1">
Data {{$index + 1}}:
<div ng-repeat="y in x.Segments">
<div>Flight No: {{y.FlightNumber}}</div>
</div>
<br/>
</div>
</div>
I created an example here:
http://codepen.io/ackzell/pen/ENBymo
It ultimately looks like this, but check the pen as it has some more info:
<ul>
<li ng-repeat="flight in vm.flightData">
<ul>
<li ng-repeat="segment in flight.Segments">
<em>FlightNumber</em> {{ segment.FlightNumber }}
<br />
<em>Departure:</em> {{ segment.DepartureAirport }}
<br />
<em>Arrival:</em> {{ segment.ArrivalAirport }}
</li>
</ul>
</li>
</ul>
Nesting ng-repeats would help, but maybe you want to give your data some treatment first.

Knockout.js : ObservableArray with ObservableArrays inside?

I'm using this plugin called Dragula which needs an ObservableArray as a source for the data..
HTML/Knockout-bindings
<div class="widget-container">
<div class="widget-content visible" id="team-setup">
<div class="header">
<p>Lagoppsett</p>
<p></p>
</div>
<div class="col-sm-12">
<div class="row">
<div class="widget-container">
<div class="col-xs-6 widget-content visible">
<div class="header">
<p>Tilgjengelige spillere</p>
<p></p>
</div>
<div class="player-card-container-mini" data-bind="dragula: { data: availablePlayers, group: 'playerz' } ">
<div class="player-card-mini">
<div class="player-card-left">
<div class="player-avatar" style="margin-left: 85%;">
<img src="Content/Images/player-female.png" id="imgAvatar" runat="server" />
<div class="player-shirt-no" data-bind="text: ShirtNo"></div>
</div>
</div>
<div class="player-card-subtext">
<div class="player-text">
<div class="player-card-header-small" data-bind="text: PlayerName"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-6 widget-content visible">
<div class="header">
<p>Lag</p>
<p></p>
</div>
<div data-bind="foreach: teamsetup">
<div data-bind="foreach: SubTeams">
<h1 data-bind="text: TeamSubName"></h1>
<div class="player-card-container-mini" data-bind="dragula: { data: Players, group: 'playerz' } " style="border: 1px solid red; min-height:200px">
<div class="player-card-mini">
<div class="player-card-left">
<div class="player-avatar" style="margin-left: 85%;">
<img src="Content/Images/player-female.png" id="img1" runat="server" />
<div class="player-shirt-no" data-bind="text: ShirtNo"></div>
</div>
</div>
<div class="player-card-subtext">
<div class="player-text">
<div class="player-card-header-small" data-bind="text: PlayerName"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="clear:both"> </div>
</div>
</div>
Knockout code :
var TeamSetupViewModel = function () {
var self = this;
self.teamsetup = ko.observableArray();
self.availablePlayers = ko.observableArray();
self.testPlayers = ko.observableArray();
}
var model = new TeamSetupViewModel();
ko.applyBindings(model, document.getElementById("team-setup"));
var uri = 'api/MainPage/GetTeamSetup/' + getQueryVariable("teamId");
$.get(uri,
function (data) {
model.teamsetup(data);
model.availablePlayers(data.AvailablePlayers);
model.testPlayers(data.AvailablePlayers);
console.log(data);
}, 'json');
});
The problem is... that i'm having a ObservableArray at the top node, and i do need ObservableArrays further down in the hierarchy.
model.availablePlayers works fine, but when accessing the other players in the html/ko foreach loops through teamsetup -> SubTeams -> Players it doesn't work due to Players isn't an ObservableArray. (There might be everyting from 1 to 7 SubTeams with players).
So how can i make the Players in each SubTeams an ObservableArray ?
See the image for the datastructure :
You could use Mapping plugin, but if players is the only thing you need, you can do it manually:
Simplify your view model:
var TeamSetupViewModel = function () {
var self = this;
self.availablePlayers = ko.observableArray();
self.subTeams = ko.observableArray();
}
After you get the data from the server, populate the view model converting the array of players on every team to an observable array of players:
$.get(uri,
function (data) {
model.availablePlayers(data.AvailablePlayers);
model.subTeams(data.SubTeams.map(function(t) { t.Players = ko.observableArray(t.Players); return t; }));
}, 'json');
});
Finally, remove the following line in your template (with its closing tag) - nothing to iterate over anymore:
<div data-bind="foreach: teamsetup">
... and update the name of the property in the next line, so it is camel case like in the VM:
<div data-bind="foreach: subTeams">

Search data on click event of button using smart table

I am very new to the smart table. I have gone through its documentation on Smart Table.
But the I haven't found how to bind data on click event in smart table?
Code is very big but I am trying to post it here.
<div class="table-scroll-x" st-table="backlinksData" st-safe-src="backlinks" st-set-filter="myStrictFilter">
<div class="crawlhealthshowcontent">
<div class="crawlhealthshowcontent-right">
<input type="text" class="crserachinput" placeholder="My URL" st-search="{{TargetUrl}}" />
<a class="bluebtn">Search</a>
</div>
<div class="clearfix"></div>
</div>
<br />
<div class="table-header clearfix">
<div class="row">
<div class="col-sm-6_5">
<div st-sort="SourceUrl" st-skip-natural="true">
Page URL
</div>
</div>
<div class="col-sm-2">
<div st-sort="SourceAnchor" st-skip-natural="true">
Anchor Text
</div>
</div>
<div class="col-sm-1">
<div st-sort="ExternalLinksCount" st-skip-natural="true">
External<br />Links
</div>
</div>
<div class="col-sm-1">
<div st-sort="InternalLinksCount" st-skip-natural="true">
Internal<br />Links
</div>
</div>
<div class="col-sm-1">
<div st-sort="IsFollow" st-skip-natural="true">
Type
</div>
</div>
</div>
</div>
<div class="table-body clearfix">
<div class="row" ng-repeat="backlink in backlinksData" ng-if="backlinks.length > 0">
<div class="col-sm-6_5">
<div class="pos-rel">
<span class="display-inline wrapWord" tool-tip="{{ backlink.SourceUrl }}"><b>Backlink source:</b> <a target="_blank" href="{{backlink.SourceUrl}}">{{ backlink.SourceUrl }}</a></span><br />
<span class="display-inline wrapWord" tool-tip="{{ backlink.SourceTitle }}"><b>Link description:</b> {{ backlink.SourceTitle }}</span> <br />
<span class="display-inline wrapWord" tool-tip="{{ backlink.TargetUrl }}"><b>My URL:</b> <a target="_blank" href="{{backlink.TargetUrl}}">{{ backlink.TargetUrl }}</a></span><br />
</div>
</div>
<div class="col-sm-2">
<div class="pos-rel">
{{ backlink.SourceAnchor }}
</div>
</div>
<div class="col-sm-1">
<div>
{{ backlink.ExternalLinksCount }}
</div>
</div>
<div class="col-sm-1">
<div>
{{ backlink.InternalLinksCount }}
</div>
</div>
<div class="col-sm-1">
<div ng-if="!backlink.IsFollow">
No Follow
</div>
</div>
</div>
<div class="row" ng-if="backlinks.length == 0">
No backlinks exists for selected location.
</div>
</div>
<div class="pos-rel" st-pagination="" st-displayed-pages="10" st-template="Home/PaginationCustom"></div>
</div>
and my js code is here.
module.controller('backlinksController', [
'$scope','$filter', 'mcatSharedDataService', 'globalVariables', 'backlinksService',
function ($scope,$filter, mcatSharedDataService, globalVariables, backlinksService) {
$scope.dataExistsValues = globalVariables.dataExistsValues;
var initialize = function () {
$scope.backlinks = undefined;
$scope.sortOrderAsc = true;
$scope.sortColumnIndex = 0;
};
initialize();
$scope.itemsByPage = 5;
var updateTableStartPage = function () {
// clear table before loading
$scope.backlinks = [];
// end clear table before loading
updateTableData();
};
var updateTableData = function () {
var property = mcatSharedDataService.PropertyDetails();
if (property == undefined || property.Primary == null || property.Primary == undefined || property.Primary.PropertyId <= 0) {
return;
}
var params = {
PropertyId: property.Primary.PropertyId
};
var backLinksDataPromise = backlinksService.getBackLinksData($scope, params);
$scope.Loading = backLinksDataPromise;
};
mcatSharedDataService.subscribeCustomerLocationsChanged($scope, updateTableStartPage);
}
]);
module.filter('myStrictFilter', function ($filter) {
return function (input, predicate) {
return $filter('filter')(input, predicate, true);
}
});
But It is working fine with the direct search on textbox.
but according to the requirement I have to perform it on button click.
Your suggestions and help would be appreciated.
Thanks in advance.
You can search for a specific row by making some simple tweaks.
add a filter to the ng-repeat, and filter it by a model that you will insert on the button click, like so: <tr ng-repeat="row in rowCollection | filter: searchQuery">
in your view, add that model (using ng-model) to an input tag and define it in your controller
then pass the value to the filter when you click the search button
here's a plunk that demonstrates this
you can use filter:searchQuery:true for strict search
EDIT:
OK, so OP's big problem was that the filtered values wouldn't show properly when paginated, the filter query is taken from an input box rather then using the de-facto st-search plug-in, So I referred to an already existing issue in github (similar), I've pulled out this plunk and modified it slightly to fit the questioned use case.

Returning length of scope variable in AngularJS

I want to return the value length of a scope variable who contains an array of objects. Well, my idea was to reduce the html view and to store the variables in my controller. The problem is when I call the value in the console I'm getting always the length 0.
The following code considered the previous view:
<div class="panel panel-default">
<div class="panel-body bg-form">
<div class="col-sm-4">
<strong>total:</strong>
</div>
<div class="col-sm-3">
<span class="badge"> {{ nameslist.length }}</span>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-body bg-form">
<div class="col-sm-4">
<strong>filtered:</strong>
</div>
<div class="col-sm-3">
<span class="badge"> {{ filteredNames.length }}</span>
</div>
</div>
</div>
...
<tr ng-repeat="names in filteredNames = (nameslist | orderBy:sortType:sortReverse)" class="show-cursor">
<td>{{ names.fname }}</td>
<td>{{ names.lname }}</td>
</tr>
These should be the new code:
<div class="panel panel-default" ng-repeat="sResult in searchResult">
<div class="panel-body bg-form">
<div class="col-sm-4">
<strong>{{ sResult.title }}:</strong>
</div>
<div class="col-sm-3">
<span class="badge"> {{ sResult.content }}</span>
</div>
</div>
</div>
Ctrl:
$scope.nameslist = CrudService.getAllNames();
$scope.searchResult = [
{
title: 'total',
content: $scope.nameslist.length
},
{
title: 'filtered',
content: $scope.filteredNames.length
}
];
Service:
myApp.factory('CrudService', ['ResService',
function (ResService) {
return {
getAllNames: function () {
return ResService.names.query();
},
...
}]);
myApp.factory('ResService', ['$resource', 'baseUrl',
function ($resource, baseUrl) {
return {
names: $resource(baseUrl + '/api/names/:Id/:fname', {
Id: '#Id',
fname: '#fname'
}, {
'update': {
method: 'PUT'
}
}),
...
}]);
How can I return only the number of the array and the filtered number of the array?
This line does not immediately populate the object;
$scope.nameslist = CrudService.getAllNames();
From the angular documentation on $resource:
It is important to realize that invoking a $resource object method
immediately returns an empty reference (object or array depending on
isArray). Once the data is returned from the server the existing
reference is populated with the actual data.
You need to bind to the object $scope.namelist in your view, or, wait until the promise is resolved and then assign the properties in the success callback.
One solution is as follows;
Javascript
$scope.searchResult = [{
title: 'total',
content: $scope.nameslist // holds a reference to the item returned by CrudService
}]
View
<div class="col-sm-4">
<strong>{{ searchResult.title }}:</strong>
</div>
<div class="col-sm-3">
<span class="badge"> {{ searchResult.content.length }}</span>
</div>

Knockout JS overwriting all values with the last value in foreach binding

In my KnockoutJS app, I am looping over an observable array and displaying some stuff like this:
<div id="user-list-container" data-bind="foreach: users">
<div class="row order-line list-row">
<div class="medium-7 small-10 columns">
<i class="fi-torso tip-right"></i>
</div>
<div class="medium-3 columns">
<a href="#" class="button split tiny info radius">
<i data-bind="text:role"></i>
<span data-dropdown="leftDrop" data-options="align:left"></span>
</a>
</div>
<div class="medium-2 small-2 columns">
<i class="fi-trash" title="#Texts.Remove"></i>
</div>
<ul id="leftDrop" class="f-dropdown" data-dropdown-content>
<li>Foreman</li>
<li>Worker</li>
</ul>
</div>
</div>
Everything works fine and all the elements are shown, but when I click one item to operate on that particular item, each item then has the value of last item in the array.
In my Javascript function:
self.makeWorker = function (user) {
var project = self.selectedProject();
var account = self.accounts.findByKey(user.accountId);
var ur = user;
console.log(user);
console.log(this);
if (project.role() != "Worker") {
var data = {
role: "Worker",
organizationId: project.organizationId,
projectId: project.id,
accountId: user.accountId
}
TippNett.Project.ChangeRole(data, function (result) {
project.users.findByKey(user.id).role("Worker");
ur.role("Worker");
account.roles.findByKey(user.id).role("Worker");
});
}
}
The value passed to the function is always the last value in 'users' observable array.
Any input on why this is happening? See the image for more briefing:

Categories