I have a couple of questions for accessing model data in my html page using angular. I'm new to angular and have been trying different things but have not been successful so far.
I've set up a contrived example in plunker to illustrate my use case.
My js file looks likes this:
angular.module("myApp", ['ui.bootstrap']);
function AccordionDemoCtrl($scope) {
jsonData = {
"myLocation": "montreal",
"locations" : [
"montreal",
"new york"
],
"carDealers": [
{
"model" : "bmw",
"location": "montreal"
},
{
"model" : "honda",
"location": "new york"
}
]
};
$scope.pageData = {
data: jsonData,
detailContentModel: 'bmw'
}
}
jsonData is the data i obtain from the server. I had trouble putting my formatted HTML code here, so i've linked to my working plunker example below.
I have 2 issues i'm trying to solve:
In my html page i'd like to display car details i.e. model and location in a div based on a user's car selection from an accordion like menu.
The detail div has two elements where
1) i'd like to display the current location of the car and
2) then a dropdown which displays all other locations except for the location associated with the selected car.
e.g.
In my example if a BMW was selected, then the detail div should display the following:
Car Location: montreal
Change location to: [This dropdown should display all locations except montreal]
I'm not entirely sure how to do this. It almost feels like i need to use a xpath type expression to access specific data elements from my model data.
Link to my plunker example.
Note: I'll try to update this question with a few code samples of what i tried - unsuccessfully, once i get a hang of the code formatting here.
Please ignore the formatting of the page. Still new to this stuff..
I think you set up the plnkr quick so I'm assuming you wouldn't really write out all your html for every model and use ng-switch and that was just for speed's sake or else I don't know why you wouldn't hard-code the location too, so when you click on a dealer in my plnkr it sets pageData.selectedDealer and uses it's details (plnkr)
For displaying the location I don't see your problem. Just put the value in braces like you do for the model:
Location: {{pageData.selectedDealer.location}}
To prevent the current location from showing up in your list you could use a filter to remove it:
var app = angular.module("myApp", ['ui.bootstrap']);
app.filter('exclude', function() {
return function(input, excludeValue) {
var result = [];
for (var i = 0; i < input.length; i++) {
if (input[i] != excludeValue) {
result.push(input[i]);
}
}
return result;
}
});
Then use the pipe character | with the filter name, a colon and the arguments to the filter:
<select
ng-options="location for location in pageData.data.locations | exclude:pageData.selectedDealer.location"
ng-model="newLocation" ></select>
Related
I want to change the attribute “Nr” of Current User using a custom action on a widget (by click on marker).
I tried to configure this code:
let attributeService = widgetContext.$injector.get(widgetContext.servicesMap.get('attributeService'));
let attributes = [{
"key": "Nr",
"value": Nr
}];
attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributes).
subscribe(
() => {
console.log('Saved!');
},
error => {
console.log('Error');
}
);
but if I insert another Entity-Id manually instead of “entityId”, I get the error “No enum constant org.thingsboard.server.common.data.EntityType.undefined” when I use the action.
I also do not know the Entity-Id of Current User (I think there is none?).
Do you know how I could solve this problem?
Thanks!
Context: I have a dashboard with a map with buildings on the left side and detail-widgets of the buildings on the right side. When I click on the buildings on the map, I want to filter the details, so that just the details of that clicked building remain. My idea is to build in a dynamic filter with the Key name “Nr” from Current User. If you click on the marker of a building, the attribute “Nr” of current user would change to the number of the clicked building and the widgets on the right would only show the information of the clicked building.
If I use the “New Update Multiple Attributes” Widget to change the attribute “Nr” everything works fine, but I want to use the map to filter the detail-Widgets.
Not exactly the answer to your question, but an easier approach to accomplish your usecase, which is provided by ThingsBoard without relying on custom actions:
https://thingsboard.io/docs/pe/user-guide/ui/widget-actions/#update-current-dashboard-state
Just use a second Entity-Alias of Type "Entity From Dashboard State". This alias is updated each time you click on a Marker in the Map (Action "Update Current Dashboard State"). Then you don't need to update any attributes.
I got you.
Thingsboard needs as "entityId" a JSON that looks like this:
entityId={
"id": the ID you are tring to put in manually,
"entityType": the type of the entity (written like this "ASSET", "DEVICE" ecc...)
}
you are giving him the Id of the entity directly so he is looking for the entityType, thats what that errors message says
to get the current user Id use: "self.ctx.currentUser.userId"
or if you are in a custom action: "widgetContext.currentUser.userId"
All together it's:
newEntityId = {};
newEntityId = {"entityType":"USER", "id":self.ctx.currentUser.userId};
attributeService.saveEntityAttributes(newEntityId, 'SERVER_SCOPE', attributes).
subscribe(
() => {
console.log('Saved!');
},
error => {
console.log('Error');
}
);
bye!
Using the Javascript, Vue.js and Typeahead.js way shown by Jeffery Way and Algolia docs to index json objects from the Algolia platform.
Currently when i search for the result i require and hit the submit button, It is passing the name attribute through in the post request.
How is it possible to pass the name and the id attribute or if nescessery just the ID arrtibute would work.
<script>
new Vue({
el: 'body',
data: {
query: '',
users: []
},
ready: function(){
this.client = algoliasearch("MYID", "MYAPI");
this.index = this.client.initIndex('dev_category_list');
$('#typeahead').typeahead(null, {
source: this.index.ttAdapter(),
displayKey: 'name'
});
},
methods: {
search: function(){
if (this.query.length < 3) return;
this.index.search(this.query, function(error, results){
this.users = results.hits;
}.bind(this));
}
}
})
</script>
As a total newcomer the laravel, vuejs and javascript its somewhat difficult to get to grips with the syntax and phrases used when explaining the instructons within the docs so any help would be greatly appreciated.
My indexed objects look like so:
{
"id": "3",
"name": "My Product 3",
"value": "3",
"alternative_name": "Prod 3",
"objectID": "3"
}
Im hoping to pass the value of ID or objectID along with the name attribute in a post request once the user selects the given result from the algolia drop down menu and hits the submit, just the ID would work as mentioned above if its not possible.
Thanks.
--- Update referencing Jerska: ---
Ok so after playing around as a newbie for a little bit i seem to have it working, Im not exactly sure how secure this is or how reliable one would say, Hopefully im not a million miles off from where i need to be. Be good to see what your personal and professional thoughts are.
.on('typeahead:select', function (e, suggestion) {
$('#typeahead').change(function() {
var val = suggestion.id;
$('#st').val(val);
});
I have created a hidden input field and named is 'st' for a demo and on update of the algolia results jquery is using the .change function to append the value to the hidden input fields value. That way the form can be continued and sibmitted as i originally wanted and hoped for, The benefit here is that even if the user is to select a result from algoia drop down menu then continue with the form, If he or she decides they want to go back to the search field and change it, They can do before they submit the form or before any window.location is run, I even thought of using ajax or simply jquery $.post but its working ok the .change
Look forward to hearing your thoughts.
Thanks
If you want to redirect to an item page, the typeahead:select event gives you the selected option :
$('#your-input')
.typeahead(/* ... */)
.on('typeahead:select', function (e, suggestion) {
window.location = suggestion.url;
});
from Algolia redirect to search results
You can access the selected object through the suggestion parameter. You could definitely use something like
window.location = 'YOUR_URL?id=' + suggestion.id + '&name=' + suggestion.name;
This assumes you're using typeahead.js#0.11.
typeahead.js being unmaintained, Algolia recommends to use their own fork of typeahead.js#0.10, autocomplete.js.
Here are the different event names and handlers signatures depending on what library/version you're using:
typeahead.js#0.11: typeahead:select -> function($event, suggestion)
typeahead.js#0.10: typeahead:selected -> function($event, suggestion, datasetName)
autocomplete.js: autocomplete:selected -> function($event, suggestion, datasetName)
I got a question and I am also accepting to getting downvotes for this because I have not really tried something yet. The problem is I don't know how to name the problem and for what I should look for around the internet.
It's like this, I got a link to an api which (in my case) contains all provinces of china in this format:
{
"result":{
"version":"1.0",
"status":100101,
"msg":"got provinces successfully"
},
"data":[
{"province":"\u9999\u6e2f"},
{"province":"\u5317\u4eac\u5e02"}
and some more. Now I want to make a dropdown select menu which contains all this provinces as dropdown values and if one dropdown is selected it should check another URL which says if the selected province is valid or not (in my case it only can be valid because the user cannot enter something himself)
?action=api&i=getCityForProvince&data=北京市
This would be the url for checking this, if it is successful it shows me the cities of the province in the same format like the code above. With this i want to make another select box which only appears if the first is true. In this select box you then select your city and that's it.
I sadly have absolutely no idea how to start with this problem and for what i should look for to solve this.
I wonder if the fact that it's chinese has anything to do with your problem? I bet, it doesn't. With jquery it's pretty easy to accomplish such tasks. It's like building blocks you need to put together.
Learn how to make ajax calls with JQuery. It's quite easy, also it should process your Json result, making it a object or array. So in the callback, you can build up your select box like described here. Another block is to bind to the change event of the select box, which is doing another Ajax call (you already know that now) using the value from the Select input. And in the result of that callback, you can also check the result json and if the result was successful, you can easily fill up another select box using already known methods now, or change its visiblity according to your results.
I think you will want to learn those things, and was not supposed to get a ready coded solution :)
To make your work easier, I recommend you to use:
a template library
an MVVM framework
The difference between using jQuery directly and an MVVM library, or template library, like handlebars or mustache, is that with jQuery you have to take care of handling the elements, and with the other solutions, you leave this work to the framework.
ANyway, I recommend using knockout over using the template libraries, because:
it includes the templates
it can provide a two-way binding
it can handle events
it can apply classes, modify visibility, enable and disable elements...
Here I add a simple example of what it can do:
// This is the ko part:
// This is the view model using Revealing Module Pattern to build it
var vm = (function(){
// The list of provinces which will be shown when available
var provinces = ko.observableArray([]);
// The province selected in the list
var selectedProvince = ko.observable();
// This is what you'd call when the provinces are loaded using AJAX
var loadProvinces = function(data) {
provinces(data);
};
// This functions will be triggered when the selected province changes...
var updateCities = function() {
console.log("Here you'd update cities");
};
// ... because of this subscription
selectedProvince.subscribe(updateCities);
// return the object with the desired properties:
return {
provinces: provinces,
selectedProvince: selectedProvince,
loadProvinces: loadProvinces,
updateCities: updateCities
};
})();
ko.applyBindings(vm);
// AJAX call simulation:
// the returned data
var data = [
{"province":"\u9999\u6e2f"},
{"province":"\u5317\u4eac\u5e02"}
];
// a time out to load the data (simulate AJAX call)
setTimeout(function() { vm.loadProvinces(data);}, 1000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="visible: !(provinces().length)">
Please wait while loading provinces
</div>
<div data-bind="visible: provinces().length">
<select data-bind="options: provinces,
optionsText: 'province',
optionsValue: 'province',
value: selectedProvince">
</select>
<p>Selected province: <span data-bind="text: selectedProvince"></span></p>
</div>
As you can see, it handles not only creating the DOM elements, bu also handling events, two way-bindig, detecting changes in values...
You could originally write the HTML for your second checkbox and give it a display: none; property. Then on the JS:
if (firstcheckboxValue === true) {
document.getElementById('secondCheckboxId').style='display: block';
}
You could use display: inline-block; or display: inline; etc, whatever suits your layout better.
Things would drastically get easier if you used jQuery. Since there's no code to start working with, I'll just list out steps I'd go through.
1) Write DOM elements for dropdowns, say #dd_provinces #dd_cities. #dd_cities would be hidden.
2) From $().ready(function(){...}) I'd make the web API call.
3) From result callback of the API call in #2, make the second API call(one to fetch cities of the province).
4) Result callback of the second API callback will populate the DOM element #dd_cities
5) Unhide #dd_cities
Sample code:
HTML
<select id="dd_provinces">
</select>
<select id="dd_cities" style="visibility: hidden">
</select>
JS
$(document).ready(function() {
$.ajax({
url: "/echo/json/",
data: "",
success: function(evt) {
var mData = ["City 1", "City 2", "City 3", "City 4"];
for(var i = 0; i < mData.length; i++){
var optionElem = "<option>" + mData[i] + "</option>";
$("#dd_provinces").append(optionElem);
}
$.ajax({
url: "/echo/json",
data: "",
success: function(evt) {
$("#dd_cities").css("visibility", "visible").animate('5000');
}
});
},
error: function(evt) {
console.log(evt);
}
});
});
I've seen other posts in this site regarding the same issue and I've tried the solutions given. I've also visited the links that may offer a solution but I'm still stuck with the same error.
I'm using DOJO and something as simple as this won't even work
myStore.newItem({id: 'test', otherfield: 'otherinfohere'});
myStore.save();
Supposedly the "newItem() was not passed an identity for the new item" error appears when you haven't provided an identifier for the new item, which i have.
The whole purpose of this (Just in case anyone can provide a good idea or has done something similar before) is that i want to create a data grid that shows info from a particular store. The problem is, that in that store all the items may not have the same structure. For instance:
I may have a store that looks like this
{identifier: 'id',
label: 'name',
items: [
{ id:'1', name:'Ecuador', capital:'Quito' },
{ id:'2', name:'Egypt', capital:'Cairo' },
{ id:'3', name:'El Salvador', capital:'San Salvador' , additionalField: 'otherinfohere'},
{ abbr:'gq', name:'Equatorial Guinea', capital:'Malabo', additionalField: 'otherinfohere'},
]}
This is possible because I'm the one constructing the store in a Spring Controller (I'm also using the Spring Framework) from information I have locally stored in a Berkeley DB. So what i need is a data grid with a dynamic layout because I don't want blank spaces to show in the view in the rows with lesser amount of fields, and i need to show all the info in the store at the same time, but i don't know how to do this.
I thought of doing it by creating a simple layout of only 1 field. In it I would load data from a store i create dynamically at runtime. The data in the store would be composed of HTML combined with the values coming from the original store so I could obtain something like this, which is inside an attribute of a JavaScript Object and let the browser parse it for me:
<div><span>id: originalID </span>....</div>
This of course is a simple example, the html layout i'm looking for is far more complicated, but i think that passing it as a string to an object might do the trick.
The problem is that i don't even know if that idea will work because i get that error whenever i try to add values to my secondary store.
rdb.modules.monitor.historicStore.fetch({onComplete: function(items, request){
for (var i = 0; i < items.length; i++){
var item = items[i];
var obj = new Object();
obj.id = rdb.modules.monitor.historicStore.getValue(item, "id");;
var html = "<div><span>";
html += rdb.modules.monitor.historicStore.getValue(item, "sql");
html += "</span></div>";
obj.html = html;
myStore.store.newItem(obj);
}
}});
In this context "historicStore" refers to the JSON store that has the values that i need to convert and add to "myStore" after i added some HTML.
I hope you got the main idea of what I'm trying to do. If anyone can help me we either of these problems i would really appreciate it. Thanks in advance
For the issue regarding store:-
"id" is mandatory for a store, if it is going to be used for a grid(datagrid, EnhancedGrid, etc. whatever). The items are handled only on basis of "id" attribute by the grid data structures.
Usually, id can be a loop variable/ auto incrementation, to avoid any cases like you have said. Before adding the store to the grid, ensure that all items have the id attribute. You can write a function which will loop through each item and check for this, else add an auto-incrementing value for the id attribute of that item.
Ok, first a simplified portion of the Javascript:
//the network values below are later dynamically
//reloaded at runtime from a remote API
$scope.networks = [{id: 5, name: "Network 1"}];
$scope.campaign = {
paths: [
{offers: [
{name: "Site1",
network: $scope.networks[0],
uri: "http://uri1.com"},
{name: "Site2",
network: $scope.networks[0],
uri: "http://uri2.com"}]}]};
And the relevant part of the HTML:
<div ng-repeat "path in campaign.paths">
<div ng-repeat="offer in path.offers">
<input type="text" ng-model="offer.name" />
<input type="text" ng-model="offer.uri" />
<select ng-model="offer.network"
ng-options="n.id as n.name for n in networks">
</select>
</div>
</div>
The problem is that this always gives me a blank option as the first option whenever I try to set the model for the to offer.network. I have read
Why does AngularJS include an empty option in select?
and
AngularJS - extra blank option added using ng-repeat in select tag, but they only seem to apply to selects that are at the top level (i.e., not using a model inside an ng-repeat). My problem is that I need to update a model that is part of a nested ng-repeat.
EDIT: I should have also included the below code; this is what is actually breaking everything. This code runs after the original array is set up:
$scope.getNetworks = function () {
$http.get('/api/networks/nameid'
).success(function (data, status, headers, config) {
$scope.networks = data;
});
};
$scope.getNetworks();
The above backend call returns an array of objects of the form {id: id, :name "name"}.
And yes, I could populate the select statically from the server side, but I would really like to know how to make this work in the general case.
Since you use the n.id in the comprehension expression of the drop down list, you should pre-populate the offer.network with the id value like this
network: $scope.networks[0].id
And since the data is populated dynamically, you can do something like this. (For the demo purpose, I just use a very dumb way to populate the values. But you get the idea.)
$scope.getNetworks().then(function(data){
$scope.networks = data;
//populate the default value
$scope.campaign.paths[0].offers[0].network = $scope.networks[0].id;
$scope.campaign.paths[0].offers[1].network = $scope.networks[0].id;
})
Working Demo