Asked  7 Months ago    Answers:  5   Viewed   17 times

I have a few checkboxes:

<input type='checkbox' value="apple" checked>
<input type='checkbox' value="orange">
<input type='checkbox' value="pear" checked>
<input type='checkbox' value="naartjie">

That I would like to bind to a list in my controller such that whenever a checkbox is changed the controller maintains a list of all the checked values, for example, ['apple', 'pear'].

ng-model seems to only be able to bind the value of one single checkbox to a variable in the controller.

Is there another way to do it so that I can bind the four checkboxes to a list in the controller?

 Answers

40

There are two ways to approach this problem. Either use a simple array or an array of objects. Each solution has it pros and cons. Below you'll find one for each case.


With a simple array as input data

The HTML could look like:

<label ng-repeat="fruitName in fruits">
  <input
    type="checkbox"
    name="selectedFruits[]"
    value="{{fruitName}}"
    ng-checked="selection.indexOf(fruitName) > -1"
    ng-click="toggleSelection(fruitName)"
  > {{fruitName}}
</label>

And the appropriate controller code would be:

app.controller('SimpleArrayCtrl', ['$scope', function SimpleArrayCtrl($scope) {

  // Fruits
  $scope.fruits = ['apple', 'orange', 'pear', 'naartjie'];

  // Selected fruits
  $scope.selection = ['apple', 'pear'];

  // Toggle selection for a given fruit by name
  $scope.toggleSelection = function toggleSelection(fruitName) {
    var idx = $scope.selection.indexOf(fruitName);

    // Is currently selected
    if (idx > -1) {
      $scope.selection.splice(idx, 1);
    }

    // Is newly selected
    else {
      $scope.selection.push(fruitName);
    }
  };
}]);

Pros: Simple data structure and toggling by name is easy to handle

Cons: Add/remove is cumbersome as two lists (the input and selection) have to be managed


With an object array as input data

The HTML could look like:

<label ng-repeat="fruit in fruits">
  <!--
    - Use `value="{{fruit.name}}"` to give the input a real value, in case the form gets submitted
      traditionally

    - Use `ng-checked="fruit.selected"` to have the checkbox checked based on some angular expression
      (no two-way-data-binding)

    - Use `ng-model="fruit.selected"` to utilize two-way-data-binding. Note that `.selected`
      is arbitrary. The property name could be anything and will be created on the object if not present.
  -->
  <input
    type="checkbox"
    name="selectedFruits[]"
    value="{{fruit.name}}"
    ng-model="fruit.selected"
  > {{fruit.name}}
</label>

And the appropriate controller code would be:

app.controller('ObjectArrayCtrl', ['$scope', 'filterFilter', function ObjectArrayCtrl($scope, filterFilter) {

  // Fruits
  $scope.fruits = [
    { name: 'apple',    selected: true },
    { name: 'orange',   selected: false },
    { name: 'pear',     selected: true },
    { name: 'naartjie', selected: false }
  ];

  // Selected fruits
  $scope.selection = [];

  // Helper method to get selected fruits
  $scope.selectedFruits = function selectedFruits() {
    return filterFilter($scope.fruits, { selected: true });
  };

  // Watch fruits for changes
  $scope.$watch('fruits|filter:{selected:true}', function (nv) {
    $scope.selection = nv.map(function (fruit) {
      return fruit.name;
    });
  }, true);
}]);

Pros: Add/remove is very easy

Cons: Somewhat more complex data structure and toggling by name is cumbersome or requires a helper method


Demo: http://jsbin.com/ImAqUC/1/

Tuesday, June 1, 2021
 
Viralk
answered 7 Months ago
10

After AngularJS version 1.3 global controller function declaration is disabled

You need to first create an AngularJS module & then attach all the components to that specific module.

CODE

function Ctrl($scope) {
    $scope.age = 24;
}

angular.module('app', [])
    .controller('Ctrl', ['$scope', Ctrl]);

Specifically for your case, there is some issue with AngularJS 1.3.14 (downgrade it to 1.3.13 works fine). Though I'd prefer you to use angular 1.2.27 AngularJS 1.6.X, Which is more stable version & latest release of AngularJS.

Working Plunkr

UPDATE:

You could do your current code to working state by allow global controller declaration inside angular.config. But this isn't the correct way to run angular application.

function Ctrl($scope) {
    $scope.age = 24;
}

angular.module('app', [])
    .config(['$controllerProvider',
        function ($controllerProvider) {
            $controllerProvider.allowGlobals();
        }
    ]);
Tuesday, June 1, 2021
 
rojo
answered 7 Months ago
50

if you do not expect that your list will be recreated then you can use the same approach as you've used for Asp.Net (instead of DataSource this property in WPF is usually named ItemsSource):

this.dataGrid1.ItemsSource = list;

But if you would like to replace your list with new collection instance then you should consider using databinding.

Wednesday, July 14, 2021
 
fardjad
answered 5 Months ago
61

Wow, that's really complicated what you've got there.

This can be accomplished in a very simple way. You need a model to represent the programmer, a view model to hold a list of programmers, and simple binding to take care of the rest.

The model:

public sealed class Programmer
{
    public string Name { get; set; }
}

Its very simple. An object representing a programmer with a name. We must encapsulate the name within an object because strings are immutable in .NET. If you tried binding against a single string in a list of strings, changes wouldn't propagate.

The collection of programmers is kept in a ViewModel. In this case, I call it ViewModel, because I have no imagination. This view model contains everything that the view binds against. In this case, its the list of programmers.

public sealed class ViewModel
{
    public ObservableCollection<Programmer> Programmers { get; private set; }

    public ViewModel()
    {
        Programmers = new ObservableCollection<Programmer>();
    }
}

The ViewModel is set as the DataContext of our view. The DataContext flows down the visual tree, and we can bind against it at any point.

public MainWindow()
{
    var vm = new ViewModel();
    vm.Programmers.Add(new Programmer { Name = "Jon Skeet" });
    vm.Programmers.Add(new Programmer { Name = "Scott Guthrie" });
    DataContext = vm;
    InitializeComponent();
}

You can set the DataContext in any way you want; I'm doing it here for simplicity's sake.

In the UI, I simply bind the ListView against the list of Programmers in the ViewModel (the DataContext, unless otherwise stated, is the root of the binding path). I then bind the TextBox against the SelectedItem of the ListBox. You select a Programmer from the list, which then becomes the SelectedItem, which I can then change the Name of.

<Window
    x:Class="Programmers.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:t="clr-namespace:Programmers"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox
            x:Name="list"
            ItemsSource="{Binding Programmers}"
            DisplayMemberPath="Name" />
        <TextBox
            Grid.Column="1"
            VerticalAlignment="Top"
            Text="{Binding SelectedItem.Name, ElementName=list}" />
    </Grid>
</Window>

Simple, once you get the hang of it.

Thursday, July 29, 2021
 
Smandoli
answered 5 Months ago
48

Category.query() is asynchronous. It returns an empty array immediately and adds the result from the request later when the response arrives - from http://docs.angularjs.org/api/ngResource.$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. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data.

If you need to access the result in your controller you should do it in the callback function like this:

$scope.category = Category.query(function(){
  console.log('all categories - ', $scope.category.length);
});
Thursday, August 12, 2021
 
SheppardDigital
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share