Unable to access merge field values inside visualforce component in angularJs - angularjs

View:
<div ng-controller = "ClPortalRegistrationController">
<div ng-repeat="(key, value) in ObjectApiFieldsetMap">
{{key}} {{value}} //this is printing correct result
<c:FieldSetComponent objectAPIName="'{{key}}'" fieldSet="'{{value}}'" cid="'{{key}}'"
sectionTitle="Section 1" columns="2" textAlign="center"></c:FieldSetComponent>
</div>
</div>
Controller:
$scope.ObjectApiFieldsetMap = {
Applications__c: "Application_FieldSet_One",
clcommon__Collateral__c: "Collateral_FieldSet_One"
};
Now when I'm trying to access {{key}},{{value}} inside c:FieldSetComponent ,its only passing string as {{key}} and {{value}} and not the converted result. How can I access values stored inside key, value inside component?

Posting the solution which I implemented as a work around.
Turns out that you cannot access angular merge field values inside visualforce components. So instead of manipulating(segregating input into key-value pair) values inside angular controller, I have to push the logic to apex controller.
<apex:component controller="RegistrationController" access="global">
<apex:repeat value="{!ObjectApiFieldsetMap}" var="apiName">
<c:FieldSetComponent objectAPIName="{!apiName}" fieldSet="{!ObjectApiFieldsetMap[apiName]}"
cid="{!apiName}{!ObjectApiFieldsetMap[apiName]}"
columns="1" textAlign="center">
</c:FieldSetComponent>
</apex:repeat>
</apex:component>
And in my apex controller i.e RegistrationController , I have set the logic to segregate key values from a map input which I'm using inside visualforce component
global class RegistrationController {
global Map<String,String> ObjectApiFieldsetMap {
get {
ObjectApiFieldsetMap = arrangeApiFieldSetsByOrder();
return ObjectApiFieldsetMap;
}
set;
}
global Map<String,String> arrangeApiFieldSetsByOrder() {
Map<String,String> ObjectApiFieldsetMap = new Map<String,String>();
/* logic for segregation */
return ObjectApiFieldsetMap;
}
}

Related

Filter card list with Razor View

Im trying to make a filter like in AngularJS when u use:
ng-repeat="u in users | filter:searchBar">
And your input filter looks like
<input type="text" id="searchBar" placeholder="start typing" ng-model="searchBar">
But the things its that im working on MVC with Razor View and I do not know how to approach this filter.
The list of cards is made with a foreachlike this:
#foreach{ var item in Models){
<div class="card">
<div class="card-container">
some content
</div>
</div>
}
Any ideas?
You can do the filtering with ajax. Here is a server side filtering solution.
First, you should move the code which renders the result to a partial view. Let's say you created a partial view called CustomerList.cshtml. Move the list code to that.
#model IEnumerable<Customer>
#foreach (var item in Model)
{
<div class="card">
<div class="card-container">
#item.Name
</div>
</div>
}
Now in your main view, you can call this partial view and pass the data to it. Wrap the call to the partial view in a container div. Add a input element for user to enter the search key. Assuming your main view is also strongly typed to IEnumerable<Customer>
#model IEnumerable<Customer>
<input type="text" id="search" data-url="#Url.Action("Index")" />
<div id="div-items">
#Html.Partial("CustomerList",Model)
</div>
Now we need to have some javascript code which listen to the keyup event on the search input, read the value of it and make an ajax call to the server where it uses the search key and get the filtered set of data, pass that to the same partial view and return the partial view result.
You can use jQuery $.get method
$(document).ready(function () {
$("#search").keyup(function() {
var v = $(this).val();
$.get($(this).data("url"), { searchKey: v }).done(function(res) {
$("#div-items").html(res);
});
});
});
Now make sure your server action method returns the filtered data like this
public ActionResult Index(string serchKey="")
{
var items = db.Customers.AsQueryable();
if (!String.IsNullOrEmpty(searchKey))
{
items = items.Where(a => a.Name.StartsWith(searchKey));
}
var t = items.ToList();
if (Request.IsAjaxRequest())
{
return PartialView("CustomerList",t );
}
return View(t);
}
Another option is to do client side filtering. on the items. But if i am going that direction, i would choose a client side MVC framework like angular to do that for me

Dynamic checkbox from list with Thymeleaf

I try to save the values from dynamically created checkboxes:
<div>
<ul>
<li th:each="item, stat : *{users}">
<input type="checkbox" th:field="*{users[__${stat.index}__]}" th:value="${item}" />
<label th:text="${item}"></label>
</li>
</ul>
</div>
The controller provides the String items as follwing:
public List<String> getUsers() {
return users;
}
And the setter for the Strings is:
public void setUsers(final String[] users) {
for (final String string : users) {
System.out.println(string);
}
}
The values are correct shown in the html page. But when i click save button, and the setter is called, the values are empty. What can i do, where is the problem?
Any help would appreciate.
Please check out section about handlin multi-value checkboxes in Tutorial: Thymeleaf + Spring.
You should provide some model attribute (of type List<String>) containing all users possible to select. Let's call it selectableUsers.
Then it can collaborate with your form-backing bean (that one containing users) in a following manner:
<div>
<ul>
<li th:each="item : ${selectableUsers}">
<input type="checkbox" th:field="*{users}" th:value="${item}" />
<label th:for="${#ids.prev('users')}" th:text="${item}"></label>
</li>
</ul>
</div>
Note I think that getter and setter for a field should handle the same type, but they don't (getter returns List<String> however setter consumes String[])
What you are trying to do looks logical, but it does not work that way.
If you did not get it resolved you can do this instead:
In relevant method of your controller you can add list of titles for your checkboxes:
List<String> allUsers = Arrays.asList("abc","xyz"); // OR generate list dynamically
model.addAttribute("selectableUsers", allUsers);
Or add it to ModelAndView if that is what you are using.
Change your html to what was suggested by #Jakub Ch.
Change your getter and setter methods as follows:
private String users;
...
public String getUsers() {
return this.users;
}
public void setUsers(String users) {
this.users = users;
}
Then 'users' field will contain comma separated String values or their id numbers ( depending on how you set it up) indicating selected checkboxes. Then you can convert String values to array using code like below or if id numbers are stored get String values from your ArrayList.
public List<String> getStrings() {
return Arrays.asList(strings.split(","));
}
Hope it helps.

MVC refuses a connection to only one action

Below is part of the Angular controller I use to populate three cascading lists of address data, with the last being a BootStrap Tab Page widget. I have only tested on Chrome and Edge so far, with similar results.
public class AreaController : BaseController
{
private readonly AreaClient _areaClient = new AreaClient(UserHelper.CurrentUser);
private readonly AgentClient _agentClient = new AgentClient();
[HttpPost]
public JsonResult ProvincesJson()
{
return Json(_areaClient.GetProvinces().ToList());
}
[HttpPost]
public JsonResult AreasJson(int provinceId)
{
var model = _areaClient.GetAreas(provinceId).ToList();
return Json(model);
}
[HttpGet]
public JsonResult SuburbsJson(int agentId, int areaId)
{
var allBurbs = _areaClient.GetSuburbs(areaId).ToList();
var agentBurbIds = _agentClient.GetAgentSuburbs(agentId).Select(ab => ab.SuburbId).ToList();
var model = allBurbs.Select(burb => new CheckListItemModel { Id = burb.SuburbId, Label = burb.SuburbName, IsChecked = agentBurbIds.Contains(burb.SuburbId) }).ToList();
return Json(model);
}
}
ProvincesJson and AreasJson work perfectly for this partial view:
<div id="areas-and-suburbs" ng-controller="areasCtrl">
<div class="form-group">
<select id="ProvinceId" ng-model="geo.provinces.selectedId" ng-change="geo.getAreas(agentId, geo.provinces.selectedId)" class="form-control">
<option ng-repeat="item in geo.provinces" ng-value="{{item.provinceId}}" class=".area-option">{{item.provinceName}}</option>
</select>
</div>
<div class="form-group">
<select id="AreaId" ng-model="geo.areas.selectedId" ng-change="geo.setSuburbs(geo.areas.selectedId)" class="form-control">
<option ng-repeat="item in geo.areas" ng-value="{{item.areaId}}" class=".area-option">{{item.areaName}}</option>
</select>
</div>
<div class="form-group">
<div id="area-suburbs-partial">
#Html.Partial("_Suburbs")
</div>
</div>
</div>
The inner partial, _Suburbs looks like this:
$scope.geo.getSuburbs = function(agentId, areaId) {
var geoUrl = "\/Area/SuburbsJson";
$http.post(geoUrl, { areaId: 3, agentId: 1 }, postOptions)
.then(function(response) {
var model = angular.fromJson(response.data);
$scope.agentSuburbs = model.$values;
_.defer(function() {
$scope.$apply();
});
},
function error(response) {
alert("Ajax error [getSuburbs]: " + response.responseText);
});
};
Yet when the outer parial renders __Suburbs, which calls geo.setSuburbs, I get "localhost refused to connect" error in Chrome. Everything in this project is same domain, just one project, and the Provinces and Areas dropdowns cascade properly, but when I select a new Area, to trigger fetching suburbs for that area, I get the error.
I see very little difference between the three actions, so I really don't understand why a connection to the third is refused. I even removed the business logic from SuburbsJson to return a simple array of int, and called it directly from the browser, vs. from my Angular controller's Ajax logic, and I still got a refused connection.
What could be behind just this one controller action causing a refused connection?
BREAKING:
I was a touch dyslexic somewhere with the spelling of area. Fixing that solved everything on that day.
For one, you're POSTing to your GET method, so MVC won't route it for you.
HTTPGET Methods in MVC have to have the JsonRequestBehavior.AllowGet flag as the second parameter of the return Json.
Instead
// Do other stuff to get model
return Json(model, JsonRequestBehavior.AllowGet);
See this other SO post for why AllowGet is necessary

How can I change a value inside of ng-repeat after the repeat complete?

I have a JSON which provides me a user's working experiences info. But country and city's are provided in a code format (TR,DE etc.)
I am using ng-repeat to pass them into html like this
<div ng-repeat="e in experiences">
<span>{{e.Name}}</span>
<span ng-init="changeCodeToFullName(e.Country)">{{vm.CountryFullName[$index]}}</span>
</div>
I am using ng-init to convert Country Code to full name. changeCodeToFullName is an angular service written by me, Is this a correct method? If it is, I can't access the dom to change CountryFullName value. I tried to access them in JS file like vm.CountryFullName[0]="TEST" but it didn't worked. I need to use e.Country variable after, therefore I can't change the original .e.Country value.
How can I access a variable inside of ng-repeat after ng-repeat completed?
How about using a custom filter:
<div ng-repeat="e in experiences">
<span>{{e.Name}}</span>
<span>{{e.Country | changeCodeToFullName}}</span>
</div>
angular.module('App').filter('changeCodeToFullName', function(YourService) {
return function(country) {
return YourService.getFullCountryName(country)
}
})
Here's an example: http://codepen.io/rustydev/pen/YWyqJB
This is one way of doing it - but this ngInit value won't be reparsed if the list updates. Why not just format the data in the JSON request response - such as:
$http.get("json.json").success(function(data) {
$scope.exeriences = data.map(function(obj) {
//Format results;
if (obj.Country == "DE") {
obj.Country = "Germany"; //etc
}
return obj;
});
});

calling a function from ng-repeat with the object from the current scope

I'm trying to call a function (from a non event element) from a ng-repeat to feed an array of data to an autocomplete element (using https://github.com/JustGoscha/allmighty-autocomplete).
It's to generate a kind of logic system :
type(listbox) | comparator (eg:>=) (listbox) | value(autocomplete)
And several of those object can be listed on a webpage to get some complex logic
type=value && type2>value3 || ...
Depending on type and comparator, values are different.
The code so far (simplified):
<div class="comparator" ng-repeat="comp in container.comparators">
<select ng-model="comp.type"><option ng-repeat="i in type_options" value="{{i.value}}" ng-selected="{{i.value==comp.type}}">{{i.label}}</option></select>
<select ng-model="comp.comparator"><option ng-repeat="i in comp_options|filter:typeMatch(comp)" value="{{i.value}}" ng-selected="{{i.value==comp.comparator}}">{{i.label}}</option></select>
<autocomplete class="autocomplete" data="" attr-placeholder="Entrez votre valeur" click-activation="true" on-type="**updateValue**" ng-model="comp.value"></autocomplete>
</div>
updateValue is the function to call, but i need to know the current object (comp from the ng-repeat) on which i am to send the right array of value.
I tryed to send an existing array to avoid "digest loop"
$scope.updateValue = function(crit){
for(var i=0;i
I also tryed to do a function that return a function that return the array :DDDDD :
$scope.updateValue = function(crit){
return function(value/*not used*/){
for(var i=0;i<$scope.comp_options.length;i++) {
if($scope.comp_options[i].value===crit.comparator){
$scope.value_elements=$scope.comp_options[i].info;
break;
}
}
return $scope.value_elements;
};
};
Replacing the autocomplete object with :
if I console.log(comp), I see that I can get my object, but I get a digest loop ...
Is there a way to know the object of the "line" I was called from ?
Thx (i'm a total newbie in angular, but so far, i've been unable to find how to retrieve that information ... is that even possible :) ?).
Access it using $index ? Example below. You can then use the index to access it
<tr ng-repeat="user in uc.users track by $index">
<td>{{user.id}}</td>
<td>{{user.first_name}}</td>
<td>{{user.last_name}}</td>
<td>{{user.email}}</td>
<td>{{user.department}}</td>
<button ng-click="uc.open(user.id, $index);">Open</button>
</tr>

Resources